@buildonspark/spark-sdk 0.3.8 → 0.3.9

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 (72) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/bare/index.cjs +1021 -1205
  3. package/dist/bare/index.d.cts +102 -66
  4. package/dist/bare/index.d.ts +102 -66
  5. package/dist/bare/index.js +946 -1138
  6. package/dist/{chunk-5ASXVNTM.js → chunk-4YFT7DAE.js} +1 -1
  7. package/dist/{chunk-XI6FCNYG.js → chunk-JLF6WJ7K.js} +1 -1
  8. package/dist/{chunk-5HU5W56H.js → chunk-MFCM6GUD.js} +2 -2
  9. package/dist/{chunk-FP2CRVQH.js → chunk-O4C4HGQL.js} +727 -1170
  10. package/dist/{chunk-FXIESWE6.js → chunk-S55NZT4P.js} +1 -1
  11. package/dist/{chunk-3LPEQGVJ.js → chunk-WRE2T22S.js} +1 -1
  12. package/dist/{chunk-VFN34EOX.js → chunk-YEBEN7XD.js} +258 -0
  13. package/dist/{client-pNpGP15j.d.cts → client-BIqiUNy4.d.cts} +1 -1
  14. package/dist/{client-By-N7oJS.d.ts → client-BaQf-5gD.d.ts} +1 -1
  15. package/dist/debug.cjs +1009 -1194
  16. package/dist/debug.d.cts +19 -16
  17. package/dist/debug.d.ts +19 -16
  18. package/dist/debug.js +5 -5
  19. package/dist/graphql/objects/index.d.cts +3 -3
  20. package/dist/graphql/objects/index.d.ts +3 -3
  21. package/dist/index.cjs +1041 -1223
  22. package/dist/index.d.cts +7 -7
  23. package/dist/index.d.ts +7 -7
  24. package/dist/index.js +30 -24
  25. package/dist/index.node.cjs +1041 -1223
  26. package/dist/index.node.d.cts +7 -7
  27. package/dist/index.node.d.ts +7 -7
  28. package/dist/index.node.js +29 -23
  29. package/dist/{logging-DMFVY384.d.ts → logging-CXhvuqJJ.d.cts} +41 -37
  30. package/dist/{logging-DxLp34Xm.d.cts → logging-DDeMLsVN.d.ts} +41 -37
  31. package/dist/native/index.react-native.cjs +1040 -1222
  32. package/dist/native/index.react-native.d.cts +92 -56
  33. package/dist/native/index.react-native.d.ts +92 -56
  34. package/dist/native/index.react-native.js +952 -1144
  35. package/dist/proto/spark.cjs +258 -0
  36. package/dist/proto/spark.d.cts +1 -1
  37. package/dist/proto/spark.d.ts +1 -1
  38. package/dist/proto/spark.js +5 -1
  39. package/dist/proto/spark_token.d.cts +1 -1
  40. package/dist/proto/spark_token.d.ts +1 -1
  41. package/dist/proto/spark_token.js +2 -2
  42. package/dist/{spark-By6yHsrk.d.cts → spark-DOpheE8_.d.cts} +39 -2
  43. package/dist/{spark-By6yHsrk.d.ts → spark-DOpheE8_.d.ts} +39 -2
  44. package/dist/{spark-wallet.browser-CNMo3IvO.d.cts → spark-wallet.browser-CbYo8A_U.d.cts} +2 -2
  45. package/dist/{spark-wallet.browser-C1dQknVj.d.ts → spark-wallet.browser-Cz8c4kOW.d.ts} +2 -2
  46. package/dist/{spark-wallet.node-BZJhJZKq.d.cts → spark-wallet.node-4WQgWwB2.d.cts} +2 -2
  47. package/dist/{spark-wallet.node-Og6__NMh.d.ts → spark-wallet.node-CmIvxtcC.d.ts} +2 -2
  48. package/dist/tests/test-utils.cjs +984 -1094
  49. package/dist/tests/test-utils.d.cts +4 -4
  50. package/dist/tests/test-utils.d.ts +4 -4
  51. package/dist/tests/test-utils.js +7 -7
  52. package/dist/{token-transactions-C7yefB2S.d.ts → token-transactions-Bu023ztN.d.ts} +2 -2
  53. package/dist/{token-transactions-CLR3rnYi.d.cts → token-transactions-CV8QD3I7.d.cts} +2 -2
  54. package/dist/types/index.cjs +256 -0
  55. package/dist/types/index.d.cts +2 -2
  56. package/dist/types/index.d.ts +2 -2
  57. package/dist/types/index.js +2 -2
  58. package/dist/{wallet-config-BoyMVa6n.d.ts → wallet-config-Bmk2eAn8.d.ts} +31 -33
  59. package/dist/{wallet-config-xom-9UFF.d.cts → wallet-config-DQw5llqA.d.cts} +31 -33
  60. package/package.json +1 -1
  61. package/src/proto/spark.ts +348 -4
  62. package/src/services/coop-exit.ts +26 -107
  63. package/src/services/deposit.ts +12 -48
  64. package/src/services/signing.ts +62 -49
  65. package/src/services/transfer.ts +437 -722
  66. package/src/spark-wallet/proto-descriptors.ts +1 -1
  67. package/src/spark-wallet/spark-wallet.ts +64 -239
  68. package/src/spark-wallet/types.ts +2 -2
  69. package/src/spark_descriptors.pb +0 -0
  70. package/src/tests/integration/lightning.test.ts +7 -5
  71. package/src/tests/integration/static_deposit.test.ts +2 -1
  72. package/src/utils/transaction.ts +250 -249
@@ -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,
@@ -700,6 +700,7 @@ function getTxEstimatedVbytesSizeByNumberOfInputsOutputs(numInputs, numOutputs)
700
700
  }
701
701
 
702
702
  // src/utils/transaction.ts
703
+ import { hexToBytes as hexToBytes2 } from "@noble/hashes/utils";
703
704
  import { Transaction as Transaction2 } from "@scure/btc-signer";
704
705
  var INITIAL_TIMELOCK = 2e3;
705
706
  var TEST_UNILATERAL_TIMELOCK = 100;
@@ -708,9 +709,9 @@ var DIRECT_TIMELOCK_OFFSET = 50;
708
709
  var HTLC_TIMELOCK_OFFSET = 70;
709
710
  var DIRECT_HTLC_TIMELOCK_OFFSET = 85;
710
711
  var INITIAL_SEQUENCE = 1 << 30 | INITIAL_TIMELOCK;
711
- var INITIAL_DIRECT_SEQUENCE = 1 << 30 | INITIAL_TIMELOCK + DIRECT_TIMELOCK_OFFSET;
712
712
  var TEST_UNILATERAL_SEQUENCE = 1 << 30 | TEST_UNILATERAL_TIMELOCK;
713
713
  var TEST_UNILATERAL_DIRECT_SEQUENCE = 1 << 30 | TEST_UNILATERAL_TIMELOCK + DIRECT_TIMELOCK_OFFSET;
714
+ var INITIAL_ROOT_NODE_SEQUENCE = 1 << 30 | 0;
714
715
  var ESTIMATED_TX_SIZE = 191;
715
716
  var DEFAULT_SATS_PER_VBYTE = 5;
716
717
  var DEFAULT_FEE_SATS = ESTIMATED_TX_SIZE * DEFAULT_SATS_PER_VBYTE;
@@ -720,63 +721,8 @@ function maybeApplyFee(amount) {
720
721
  }
721
722
  return amount;
722
723
  }
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
724
  function createNodeTx({
725
+ sequence,
780
726
  txOut,
781
727
  parentOutPoint,
782
728
  applyFee,
@@ -786,7 +732,10 @@ function createNodeTx({
786
732
  version: 3,
787
733
  allowUnknownOutputs: true
788
734
  });
789
- nodeTx.addInput(parentOutPoint);
735
+ nodeTx.addInput({
736
+ ...parentOutPoint,
737
+ sequence
738
+ });
790
739
  if (applyFee) {
791
740
  nodeTx.addOutput({
792
741
  script: txOut.script,
@@ -800,52 +749,92 @@ function createNodeTx({
800
749
  }
801
750
  return nodeTx;
802
751
  }
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
752
+ function createNodeTxs({
753
+ parentTx,
754
+ sequence,
755
+ directSequence,
756
+ vout
757
+ }) {
758
+ const parentOutput = parentTx.getOutput(vout);
759
+ if (!parentOutput.amount || !parentOutput.script) {
760
+ throw new ValidationError("Parent output amount or script not found", {
761
+ field: "parentOutput",
762
+ value: parentOutput
816
763
  });
817
764
  }
818
- return { cpfpNodeTx, directNodeTx };
765
+ const output = {
766
+ script: parentOutput.script,
767
+ amount: parentOutput.amount
768
+ };
769
+ const input = {
770
+ txid: hexToBytes2(getTxId(parentTx)),
771
+ index: vout
772
+ };
773
+ const nodeTx = createNodeTx({
774
+ sequence,
775
+ txOut: output,
776
+ parentOutPoint: input,
777
+ includeAnchor: true
778
+ });
779
+ const directNodeTx = createNodeTx({
780
+ sequence: directSequence ?? sequence + DIRECT_TIMELOCK_OFFSET,
781
+ txOut: output,
782
+ parentOutPoint: input,
783
+ includeAnchor: false,
784
+ applyFee: true
785
+ });
786
+ return { nodeTx, directNodeTx };
819
787
  }
820
- function createLeafNodeTx(sequence, directSequence, parentOutPoint, txOut, shouldCalculateFee) {
821
- const cpfpLeafTx = new Transaction2({
822
- version: 3,
823
- allowUnknownOutputs: true
788
+ function createRootNodeTx(parentTx, vout) {
789
+ return createNodeTxs({
790
+ parentTx,
791
+ sequence: INITIAL_ROOT_NODE_SEQUENCE,
792
+ vout
824
793
  });
825
- cpfpLeafTx.addInput({
826
- ...parentOutPoint,
827
- sequence
794
+ }
795
+ function createZeroTimelockNodeTx(parentTx) {
796
+ return createNodeTxs({
797
+ parentTx,
798
+ sequence: INITIAL_ROOT_NODE_SEQUENCE,
799
+ directSequence: DIRECT_TIMELOCK_OFFSET,
800
+ vout: 0
828
801
  });
829
- cpfpLeafTx.addOutput(txOut);
830
- cpfpLeafTx.addOutput(getEphemeralAnchorOutput());
831
- const directLeafTx = new Transaction2({
832
- version: 3,
833
- allowUnknownOutputs: true
802
+ }
803
+ function createInitialTimelockNodeTx(parentTx) {
804
+ return createNodeTxs({
805
+ parentTx,
806
+ sequence: INITIAL_SEQUENCE,
807
+ vout: 0
834
808
  });
835
- directLeafTx.addInput({
836
- ...parentOutPoint,
837
- sequence: directSequence
809
+ }
810
+ function createDecrementedTimelockNodeTx(parentTx, currentTx) {
811
+ const currentSequence = currentTx.getInput(0).sequence;
812
+ if (!currentSequence) {
813
+ throw new ValidationError("Current sequence not found", {
814
+ field: "currentSequence",
815
+ value: currentSequence
816
+ });
817
+ }
818
+ return createNodeTxs({
819
+ parentTx,
820
+ sequence: getNextTransactionSequence(currentSequence).nextSequence,
821
+ vout: 0
838
822
  });
839
- const amountSats = txOut.amount ?? 0n;
840
- let outputAmount = amountSats;
841
- if (shouldCalculateFee) {
842
- outputAmount = maybeApplyFee(amountSats);
823
+ }
824
+ function createTestUnilateralTimelockNodeTx(parentTx, nodeTx) {
825
+ const sequence = nodeTx.getInput(0).sequence;
826
+ if (!sequence) {
827
+ throw new ValidationError("Sequence not found", {
828
+ field: "sequence",
829
+ value: sequence
830
+ });
843
831
  }
844
- directLeafTx.addOutput({
845
- script: txOut.script,
846
- amount: outputAmount
832
+ const isBit30Defined = (sequence || 0) & 1 << 30;
833
+ return createNodeTxs({
834
+ parentTx,
835
+ sequence: isBit30Defined | TEST_UNILATERAL_TIMELOCK,
836
+ vout: 0
847
837
  });
848
- return [cpfpLeafTx, directLeafTx];
849
838
  }
850
839
  function createRefundTx({
851
840
  sequence,
@@ -901,110 +890,109 @@ function getNextHTLCTransactionSequence(currSequence, isNodeTx) {
901
890
  };
902
891
  }
903
892
  function createRefundTxs({
904
- sequence,
905
- directSequence,
906
- input,
907
- directInput,
908
- amountSats,
893
+ nodeTx,
894
+ directNodeTx,
909
895
  receivingPubkey,
910
- network
896
+ network,
897
+ sequence
911
898
  }) {
912
- const cpfpRefundTx = createRefundTx({
913
- sequence,
914
- input,
915
- amountSats,
916
- receivingPubkey,
917
- network,
918
- shouldCalculateFee: false,
919
- includeAnchor: true
920
- });
899
+ const refundInput = {
900
+ txid: hexToBytes2(getTxId(nodeTx)),
901
+ index: 0
902
+ };
903
+ const nodeAmountSats = nodeTx.getOutput(0).amount;
904
+ if (nodeAmountSats === void 0) {
905
+ throw new ValidationError("Node amount not found", {
906
+ field: "nodeAmountSats",
907
+ value: nodeAmountSats
908
+ });
909
+ }
921
910
  let directRefundTx;
922
- let directFromCpfpRefundTx;
923
- if (directSequence && directInput) {
911
+ if (directNodeTx) {
912
+ const directRefundInput = {
913
+ txid: hexToBytes2(getTxId(directNodeTx)),
914
+ index: 0
915
+ };
916
+ const directAmountSats = directNodeTx.getOutput(0).amount;
917
+ if (directAmountSats === void 0) {
918
+ throw new ValidationError("Direct amount not found", {
919
+ field: "directAmountSats",
920
+ value: directAmountSats
921
+ });
922
+ }
924
923
  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,
924
+ sequence: sequence + DIRECT_TIMELOCK_OFFSET,
925
+ input: directRefundInput,
926
+ amountSats: directAmountSats,
937
927
  receivingPubkey,
938
928
  network,
939
929
  shouldCalculateFee: true,
940
930
  includeAnchor: false
941
931
  });
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
932
  }
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
933
+ const cpfpRefundTx = createRefundTx({
934
+ sequence,
935
+ input: refundInput,
936
+ amountSats: nodeAmountSats,
937
+ receivingPubkey,
938
+ network,
939
+ shouldCalculateFee: false,
940
+ includeAnchor: true
957
941
  });
958
- cpfpRefundTx.addInput({
959
- ...cpfpNodeOutPoint,
960
- sequence
942
+ const directFromCpfpRefundTx = createRefundTx({
943
+ sequence: sequence + DIRECT_TIMELOCK_OFFSET,
944
+ input: refundInput,
945
+ amountSats: nodeAmountSats,
946
+ receivingPubkey,
947
+ network,
948
+ shouldCalculateFee: true,
949
+ includeAnchor: false
961
950
  });
962
- cpfpRefundTx.addInput(connectorOutput);
963
- const receiverScript = getP2TRScriptFromPublicKey(receiverPubKey, network);
964
- cpfpRefundTx.addOutput({
965
- script: receiverScript,
966
- amount: amountSats
951
+ return { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx };
952
+ }
953
+ function createInitialTimelockRefundTxs(params) {
954
+ return createRefundTxs({
955
+ ...params,
956
+ sequence: INITIAL_SEQUENCE
967
957
  });
968
- const directRefundTx = new Transaction2({
969
- version: 3,
970
- allowUnknownOutputs: true
958
+ }
959
+ function createDecrementedTimelockRefundTxs(params) {
960
+ const nextSequence = getNextTransactionSequence(params.sequence).nextSequence;
961
+ return createRefundTxs({
962
+ ...params,
963
+ sequence: nextSequence
971
964
  });
972
- directRefundTx.addInput({
973
- ...directNodeOutPoint,
974
- sequence
965
+ }
966
+ function createCurrentTimelockRefundTxs(params) {
967
+ return createRefundTxs(params);
968
+ }
969
+ function createTestUnilateralRefundTxs(params) {
970
+ return createRefundTxs({
971
+ ...params,
972
+ sequence: TEST_UNILATERAL_SEQUENCE
975
973
  });
976
- directRefundTx.addInput(connectorOutput);
977
- let outputAmount = amountSats;
978
- if (shouldCalculateFee) {
979
- outputAmount = maybeApplyFee(amountSats);
974
+ }
975
+ function createConnectorRefundTxs(params) {
976
+ const { connectorOutput, ...baseParams } = params;
977
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createDecrementedTimelockRefundTxs(baseParams);
978
+ cpfpRefundTx.addInput(connectorOutput);
979
+ if (directRefundTx) {
980
+ directRefundTx.addInput(connectorOutput);
980
981
  }
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];
982
+ if (directFromCpfpRefundTx) {
983
+ directFromCpfpRefundTx.addInput(connectorOutput);
984
+ }
985
+ return { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx };
999
986
  }
1000
987
  function getCurrentTimelock(currSequence) {
1001
988
  return (currSequence || 0) & 65535;
1002
989
  }
1003
990
  function getTransactionSequence(currSequence) {
1004
991
  const timelock = getCurrentTimelock(currSequence);
992
+ const isBit30Defined = (currSequence || 0) & 1 << 30;
1005
993
  return {
1006
- nextSequence: 1 << 30 | timelock,
1007
- nextDirectSequence: 1 << 30 | timelock + DIRECT_TIMELOCK_OFFSET
994
+ nextSequence: isBit30Defined | timelock,
995
+ nextDirectSequence: isBit30Defined | timelock + DIRECT_TIMELOCK_OFFSET
1008
996
  };
1009
997
  }
1010
998
  function checkIfValidSequence(currSequence) {
@@ -1023,6 +1011,13 @@ function checkIfValidSequence(currSequence) {
1023
1011
  });
1024
1012
  }
1025
1013
  }
1014
+ function isZeroTimelock(currSequence) {
1015
+ return getCurrentTimelock(currSequence) === 0;
1016
+ }
1017
+ function doesTxnNeedRenewed(currSequence) {
1018
+ const currentTimelock = getCurrentTimelock(currSequence);
1019
+ return currentTimelock <= 100;
1020
+ }
1026
1021
  function doesLeafNeedRefresh(currSequence, isNodeTx) {
1027
1022
  const currentTimelock = getCurrentTimelock(currSequence);
1028
1023
  if (isNodeTx) {
@@ -1033,6 +1028,7 @@ function doesLeafNeedRefresh(currSequence, isNodeTx) {
1033
1028
  function getNextTransactionSequence(currSequence, isNodeTx) {
1034
1029
  const currentTimelock = getCurrentTimelock(currSequence);
1035
1030
  const nextTimelock = currentTimelock - TIME_LOCK_INTERVAL;
1031
+ const isBit30Defined = (currSequence || 0) & 1 << 30;
1036
1032
  if (isNodeTx && nextTimelock < 0) {
1037
1033
  throw new ValidationError("timelock interval is less than 0", {
1038
1034
  field: "nextTimelock",
@@ -1047,8 +1043,8 @@ function getNextTransactionSequence(currSequence, isNodeTx) {
1047
1043
  });
1048
1044
  }
1049
1045
  return {
1050
- nextSequence: 1 << 30 | nextTimelock,
1051
- nextDirectSequence: 1 << 30 | nextTimelock + DIRECT_TIMELOCK_OFFSET
1046
+ nextSequence: isBit30Defined | nextTimelock,
1047
+ nextDirectSequence: isBit30Defined | nextTimelock + DIRECT_TIMELOCK_OFFSET
1052
1048
  };
1053
1049
  }
1054
1050
  function getEphemeralAnchorOutput() {
@@ -1070,7 +1066,7 @@ var KeyDerivationType = /* @__PURE__ */ ((KeyDerivationType2) => {
1070
1066
  })(KeyDerivationType || {});
1071
1067
 
1072
1068
  // src/utils/transfer_package.ts
1073
- import { hexToBytes as hexToBytes2 } from "@noble/curves/utils";
1069
+ import { hexToBytes as hexToBytes3 } from "@noble/curves/utils";
1074
1070
  import { sha256 as sha2562 } from "@noble/hashes/sha2";
1075
1071
  function getTransferPackageSigningPayload(transferID, transferPackage) {
1076
1072
  const encryptedPayload = transferPackage.keyTweakPackage;
@@ -1079,7 +1075,7 @@ function getTransferPackageSigningPayload(transferID, transferPackage) {
1079
1075
  ).map(([key, value]) => ({ key, value }));
1080
1076
  pairs.sort((a, b) => a.key.localeCompare(b.key));
1081
1077
  const encoder = new TextEncoder();
1082
- let message = hexToBytes2(transferID.replaceAll("-", ""));
1078
+ let message = hexToBytes3(transferID.replaceAll("-", ""));
1083
1079
  for (const pair of pairs) {
1084
1080
  const keyPart = encoder.encode(pair.key + ":");
1085
1081
  const separator = encoder.encode(";");
@@ -1207,7 +1203,7 @@ function proofOfPossessionMessageHashForDepositAddress(userPubkey, operatorPubke
1207
1203
  // src/services/deposit.ts
1208
1204
  import { schnorr as schnorr2, secp256k1 as secp256k13 } from "@noble/curves/secp256k1";
1209
1205
  import { sha256 as sha2564 } from "@noble/hashes/sha2";
1210
- import { hexToBytes as hexToBytes3 } from "@noble/hashes/utils";
1206
+ import { hexToBytes as hexToBytes4 } from "@noble/hashes/utils";
1211
1207
  import { p2tr as p2tr2 } from "@scure/btc-signer";
1212
1208
  import { equalBytes } from "@scure/btc-signer/utils";
1213
1209
  var DepositService = class {
@@ -1261,7 +1257,7 @@ var DepositService = class {
1261
1257
  if (operator.identifier === this.config.getCoordinatorIdentifier() && !verifyCoordinatorProof) {
1262
1258
  continue;
1263
1259
  }
1264
- const operatorPubkey2 = hexToBytes3(operator.identityPublicKey);
1260
+ const operatorPubkey2 = hexToBytes4(operator.identityPublicKey);
1265
1261
  const operatorSig = address.depositAddressProof.addressSignatures[operator.identifier];
1266
1262
  if (!operatorSig) {
1267
1263
  throw new ValidationError("Operator signature not found", {
@@ -1380,38 +1376,18 @@ var DepositService = class {
1380
1376
  expected: "Valid output index"
1381
1377
  });
1382
1378
  }
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
1379
+ const { nodeTx: cpfpRootTx, directNodeTx: directRootTx } = createRootNodeTx(
1380
+ depositTx,
1381
+ vout
1403
1382
  );
1404
1383
  const cpfpRootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
1405
1384
  const directRootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
1406
1385
  const cpfpRootTxSighash = getSigHashFromTx(cpfpRootTx, 0, output);
1407
1386
  const directRootTxSighash = getSigHashFromTx(directRootTx, 0, output);
1408
1387
  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,
1388
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createInitialTimelockRefundTxs({
1389
+ nodeTx: cpfpRootTx,
1390
+ directNodeTx: directRootTx,
1415
1391
  receivingPubkey: signingPubKey,
1416
1392
  network: this.config.getNetwork()
1417
1393
  });
@@ -1703,22 +1679,12 @@ var DepositService = class {
1703
1679
  expected: "Output with script and amount"
1704
1680
  });
1705
1681
  }
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);
1682
+ const { nodeTx: cpfpRootTx } = createRootNodeTx(depositTx, vout);
1715
1683
  const cpfpRootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
1716
1684
  const cpfpRootTxSighash = getSigHashFromTx(cpfpRootTx, 0, output);
1717
1685
  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,
1686
+ const { cpfpRefundTx } = createInitialTimelockRefundTxs({
1687
+ nodeTx: cpfpRootTx,
1722
1688
  receivingPubkey: signingPubKey,
1723
1689
  network: this.config.getNetwork()
1724
1690
  });
@@ -1879,7 +1845,7 @@ var setCrypto = (cryptoImplParam) => {
1879
1845
  import { BinaryWriter } from "@bufbuild/protobuf/wire";
1880
1846
  import { schnorr as schnorr3, secp256k1 as secp256k14 } from "@noble/curves/secp256k1";
1881
1847
  import { bytesToNumberBE as bytesToNumberBE2 } from "@noble/curves/utils";
1882
- import { bytesToHex as bytesToHex3, hexToBytes as hexToBytes4 } from "@noble/hashes/utils";
1848
+ import { bytesToHex as bytesToHex3, hexToBytes as hexToBytes5 } from "@noble/hashes/utils";
1883
1849
  import { bech32m as bech32m2 } from "@scure/base";
1884
1850
  import { UUID } from "uuidv7";
1885
1851
 
@@ -2071,7 +2037,7 @@ function encodeSparkAddress(payload) {
2071
2037
  function encodeSparkAddressWithSignature(payload, signature) {
2072
2038
  try {
2073
2039
  isValidPublicKey(payload.identityPublicKey);
2074
- const identityPublicKey = hexToBytes4(payload.identityPublicKey);
2040
+ const identityPublicKey = hexToBytes5(payload.identityPublicKey);
2075
2041
  let sparkInvoiceFields;
2076
2042
  if (payload.sparkInvoiceFields) {
2077
2043
  validateSparkInvoiceFields(payload.sparkInvoiceFields);
@@ -2575,7 +2541,7 @@ import {
2575
2541
  bytesToNumberBE as bytesToNumberBE4,
2576
2542
  numberToBytesBE as numberToBytesBE2
2577
2543
  } from "@noble/curves/utils";
2578
- import { hexToBytes as hexToBytes5 } from "@noble/hashes/utils";
2544
+ import { hexToBytes as hexToBytes6 } from "@noble/hashes/utils";
2579
2545
 
2580
2546
  // src/utils/token-hashing.ts
2581
2547
  import { sha256 as sha2566 } from "@noble/hashes/sha2";
@@ -3895,14 +3861,14 @@ var TokenTransactionService = class {
3895
3861
  }
3896
3862
  if (receiverAddress.sparkInvoiceFields) {
3897
3863
  return {
3898
- receiverPublicKey: hexToBytes5(receiverAddress.identityPublicKey),
3864
+ receiverPublicKey: hexToBytes6(receiverAddress.identityPublicKey),
3899
3865
  rawTokenIdentifier,
3900
3866
  tokenAmount: transfer.tokenAmount,
3901
3867
  sparkInvoice: transfer.receiverSparkAddress
3902
3868
  };
3903
3869
  }
3904
3870
  return {
3905
- receiverPublicKey: hexToBytes5(receiverAddress.identityPublicKey),
3871
+ receiverPublicKey: hexToBytes6(receiverAddress.identityPublicKey),
3906
3872
  rawTokenIdentifier,
3907
3873
  tokenAmount: transfer.tokenAmount
3908
3874
  };
@@ -3968,7 +3934,7 @@ var TokenTransactionService = class {
3968
3934
  for (const [_, operator] of Object.entries(
3969
3935
  this.config.getSigningOperators()
3970
3936
  )) {
3971
- operatorKeys.push(hexToBytes5(operator.identityPublicKey));
3937
+ operatorKeys.push(hexToBytes6(operator.identityPublicKey));
3972
3938
  }
3973
3939
  return operatorKeys;
3974
3940
  }
@@ -4231,8 +4197,8 @@ var TokenTransactionService = class {
4231
4197
  this.config.getCoordinatorAddress()
4232
4198
  );
4233
4199
  let queryParams = {
4234
- issuerPublicKeys: issuerPublicKeys?.map(hexToBytes5),
4235
- ownerPublicKeys: ownerPublicKeys?.map(hexToBytes5),
4200
+ issuerPublicKeys: issuerPublicKeys?.map(hexToBytes6),
4201
+ ownerPublicKeys: ownerPublicKeys?.map(hexToBytes6),
4236
4202
  tokenIdentifiers: tokenIdentifiers?.map((identifier) => {
4237
4203
  const { tokenIdentifier } = decodeBech32mTokenIdentifier(
4238
4204
  identifier,
@@ -4240,7 +4206,7 @@ var TokenTransactionService = class {
4240
4206
  );
4241
4207
  return tokenIdentifier;
4242
4208
  }),
4243
- tokenTransactionHashes: tokenTransactionHashes?.map(hexToBytes5),
4209
+ tokenTransactionHashes: tokenTransactionHashes?.map(hexToBytes6),
4244
4210
  outputIds: outputIds || [],
4245
4211
  limit: pageSize,
4246
4212
  offset
@@ -4345,7 +4311,7 @@ var TokenTransactionService = class {
4345
4311
  }
4346
4312
  const payload = {
4347
4313
  finalTokenTransactionHash,
4348
- operatorIdentityPublicKey: hexToBytes5(operator.identityPublicKey)
4314
+ operatorIdentityPublicKey: hexToBytes6(operator.identityPublicKey)
4349
4315
  };
4350
4316
  const payloadHash = await hashOperatorSpecificTokenTransactionSignablePayload(payload);
4351
4317
  const ownerSignature = await this.signMessageWithKey(
@@ -4367,7 +4333,7 @@ var TokenTransactionService = class {
4367
4333
  }
4368
4334
  const payload = {
4369
4335
  finalTokenTransactionHash,
4370
- operatorIdentityPublicKey: hexToBytes5(operator.identityPublicKey)
4336
+ operatorIdentityPublicKey: hexToBytes6(operator.identityPublicKey)
4371
4337
  };
4372
4338
  const payloadHash = await hashOperatorSpecificTokenTransactionSignablePayload(payload);
4373
4339
  const ownerSignature = await this.signMessageWithKey(
@@ -4383,7 +4349,7 @@ var TokenTransactionService = class {
4383
4349
  for (let i = 0; i < transferInput.outputsToSpend.length; i++) {
4384
4350
  const payload = {
4385
4351
  finalTokenTransactionHash,
4386
- operatorIdentityPublicKey: hexToBytes5(operator.identityPublicKey)
4352
+ operatorIdentityPublicKey: hexToBytes6(operator.identityPublicKey)
4387
4353
  };
4388
4354
  const payloadHash = await hashOperatorSpecificTokenTransactionSignablePayload(payload);
4389
4355
  let ownerSignature;
@@ -4400,7 +4366,7 @@ var TokenTransactionService = class {
4400
4366
  }
4401
4367
  inputTtxoSignaturesPerOperator.push({
4402
4368
  ttxoSignatures,
4403
- operatorIdentityPublicKey: hexToBytes5(operator.identityPublicKey)
4369
+ operatorIdentityPublicKey: hexToBytes6(operator.identityPublicKey)
4404
4370
  });
4405
4371
  }
4406
4372
  return inputTtxoSignaturesPerOperator;
@@ -4830,7 +4796,7 @@ import {
4830
4796
  bytesToHex as bytesToHex6,
4831
4797
  bytesToNumberBE as bytesToNumberBE6,
4832
4798
  equalBytes as equalBytes4,
4833
- hexToBytes as hexToBytes6
4799
+ hexToBytes as hexToBytes7
4834
4800
  } from "@noble/curves/utils";
4835
4801
  import { sha256 as sha2567 } from "@noble/hashes/sha2";
4836
4802
  import { HDKey } from "@scure/bip32";
@@ -4848,7 +4814,7 @@ var isWebExtension = (
4848
4814
  "chrome" in globalThis && globalThis.chrome.runtime?.id
4849
4815
  );
4850
4816
  var userAgent = "navigator" in globalThis ? globalThis.navigator.userAgent || "unknown-user-agent" : void 0;
4851
- var packageVersion = true ? "0.3.8" : "unknown";
4817
+ var packageVersion = true ? "0.3.9" : "unknown";
4852
4818
  var baseEnvStr = "unknown";
4853
4819
  if (isBun) {
4854
4820
  const bunVersion = "version" in globalThis.Bun ? globalThis.Bun.version : "unknown-version";
@@ -5228,7 +5194,7 @@ var DefaultSparkSigner = class {
5228
5194
  }
5229
5195
  async createSparkWalletFromSeed(seed, accountNumber) {
5230
5196
  if (typeof seed === "string") {
5231
- seed = hexToBytes6(seed);
5197
+ seed = hexToBytes7(seed);
5232
5198
  }
5233
5199
  const {
5234
5200
  identityKey,
@@ -5367,7 +5333,7 @@ var TaprootSparkSigner = class extends DefaultSparkSigner {
5367
5333
 
5368
5334
  // src/tests/utils/test-faucet.ts
5369
5335
  import { schnorr as schnorr6, secp256k1 as secp256k19 } from "@noble/curves/secp256k1";
5370
- import { bytesToHex as bytesToHex7, hexToBytes as hexToBytes7 } from "@noble/curves/utils";
5336
+ import { bytesToHex as bytesToHex7, hexToBytes as hexToBytes8 } from "@noble/curves/utils";
5371
5337
  import * as btc3 from "@scure/btc-signer";
5372
5338
  import { Address as Address2, OutScript as OutScript2, SigHash as SigHash2, Transaction as Transaction4 } from "@scure/btc-signer";
5373
5339
  import { taprootTweakPrivKey as taprootTweakPrivKey2 } from "@scure/btc-signer/utils";
@@ -5394,10 +5360,10 @@ var getFetch = () => {
5394
5360
  };
5395
5361
 
5396
5362
  // src/tests/utils/test-faucet.ts
5397
- var STATIC_FAUCET_KEY = hexToBytes7(
5363
+ var STATIC_FAUCET_KEY = hexToBytes8(
5398
5364
  "deadbeef1337cafe4242424242424242deadbeef1337cafe4242424242424242"
5399
5365
  );
5400
- var STATIC_MINING_KEY = hexToBytes7(
5366
+ var STATIC_MINING_KEY = hexToBytes8(
5401
5367
  "1337cafe4242deadbeef4242424242421337cafe4242deadbeef424242424242"
5402
5368
  );
5403
5369
  var SATS_PER_BTC = 1e8;
@@ -5480,7 +5446,7 @@ var BitcoinFaucet = class _BitcoinFaucet {
5480
5446
  );
5481
5447
  await this.generateToAddress(1, address);
5482
5448
  const fundingTxRaw = await this.getRawTransaction(fundingTxid);
5483
- const fundingTx = Transaction4.fromRaw(hexToBytes7(fundingTxRaw.hex));
5449
+ const fundingTx = Transaction4.fromRaw(hexToBytes8(fundingTxRaw.hex));
5484
5450
  for (let i = 0; i < fundingTx.outputsLength; i++) {
5485
5451
  const output = fundingTx.getOutput(i);
5486
5452
  if (!output.script || !output.amount) continue;
@@ -5546,7 +5512,7 @@ var BitcoinFaucet = class _BitcoinFaucet {
5546
5512
  this.coins.push({
5547
5513
  key: STATIC_FAUCET_KEY,
5548
5514
  outpoint: {
5549
- txid: hexToBytes7(splitTxId),
5515
+ txid: hexToBytes8(splitTxId),
5550
5516
  index: i
5551
5517
  },
5552
5518
  txout: signedSplitTx.getOutput(i)
@@ -6635,7 +6601,7 @@ function collectResponses(responses) {
6635
6601
  }
6636
6602
 
6637
6603
  // src/utils/unilateral-exit.ts
6638
- import { bytesToHex as bytesToHex8, hexToBytes as hexToBytes8 } from "@noble/curves/utils";
6604
+ import { bytesToHex as bytesToHex8, hexToBytes as hexToBytes9 } from "@noble/curves/utils";
6639
6605
  import { ripemd160 } from "@noble/hashes/legacy";
6640
6606
  import { sha256 as sha2569 } from "@noble/hashes/sha2";
6641
6607
  import * as btc4 from "@scure/btc-signer";
@@ -6650,7 +6616,7 @@ function isEphemeralAnchorOutput(script, amount) {
6650
6616
  }
6651
6617
  async function constructUnilateralExitTxs(nodeHexStrings, sparkClient, network) {
6652
6618
  const result = [];
6653
- const nodes = nodeHexStrings.map((hex) => TreeNode.decode(hexToBytes8(hex)));
6619
+ const nodes = nodeHexStrings.map((hex) => TreeNode.decode(hexToBytes9(hex)));
6654
6620
  const nodeMap = /* @__PURE__ */ new Map();
6655
6621
  for (const node of nodes) {
6656
6622
  nodeMap.set(node.id, node);
@@ -6739,7 +6705,7 @@ async function constructUnilateralExitFeeBumpPackages(nodeHexStrings, utxos, fee
6739
6705
  `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
6706
  );
6741
6707
  }
6742
- const nodeBytes = hexToBytes8(hex);
6708
+ const nodeBytes = hexToBytes9(hex);
6743
6709
  const node = TreeNode.decode(nodeBytes);
6744
6710
  if (!node.id) {
6745
6711
  throw new Error(
@@ -6853,7 +6819,7 @@ async function constructUnilateralExitFeeBumpPackages(nodeHexStrings, utxos, fee
6853
6819
  usedUtxos,
6854
6820
  correctedParentTx
6855
6821
  } = constructFeeBumpTx(nodeTxHex, availableUtxos, feeRate, void 0);
6856
- const feeBumpTx = btc4.Transaction.fromPSBT(hexToBytes8(nodeFeeBumpPsbt));
6822
+ const feeBumpTx = btc4.Transaction.fromPSBT(hexToBytes9(nodeFeeBumpPsbt));
6857
6823
  var feeBumpOut = feeBumpTx.outputsLength === 1 ? feeBumpTx.getOutput(0) : null;
6858
6824
  var feeBumpOutPubKey = null;
6859
6825
  for (const usedUtxo of usedUtxos) {
@@ -6908,7 +6874,7 @@ async function constructUnilateralExitFeeBumpPackages(nodeHexStrings, utxos, fee
6908
6874
  void 0
6909
6875
  );
6910
6876
  const feeBumpTx2 = btc4.Transaction.fromPSBT(
6911
- hexToBytes8(refundFeeBump.feeBumpPsbt)
6877
+ hexToBytes9(refundFeeBump.feeBumpPsbt)
6912
6878
  );
6913
6879
  var feeBumpOut = feeBumpTx2.outputsLength === 1 ? feeBumpTx2.getOutput(0) : null;
6914
6880
  var feeBumpOutPubKey = null;
@@ -7023,9 +6989,9 @@ function constructFeeBumpTx(txHex, utxos, feeRate, previousFeeBumpTx) {
7023
6989
  if (!fundingUtxo) {
7024
6990
  throw new Error(`UTXO at index ${i} is undefined`);
7025
6991
  }
7026
- const pubKeyHash = hash160(hexToBytes8(fundingUtxo.publicKey));
6992
+ const pubKeyHash = hash160(hexToBytes9(fundingUtxo.publicKey));
7027
6993
  const scriptToUse = new Uint8Array([0, 20, ...pubKeyHash]);
7028
- const providedScript = hexToBytes8(fundingUtxo.script);
6994
+ const providedScript = hexToBytes9(fundingUtxo.script);
7029
6995
  if (bytesToHex8(scriptToUse) !== bytesToHex8(providedScript)) {
7030
6996
  throw new Error(
7031
6997
  `\u274C Derived script doesn't match provided script for UTXO ${i + 1}.`
@@ -7139,7 +7105,6 @@ __export(utils_exports, {
7139
7105
  DIRECT_HTLC_TIMELOCK_OFFSET: () => DIRECT_HTLC_TIMELOCK_OFFSET,
7140
7106
  DIRECT_TIMELOCK_OFFSET: () => DIRECT_TIMELOCK_OFFSET,
7141
7107
  HTLC_TIMELOCK_OFFSET: () => HTLC_TIMELOCK_OFFSET,
7142
- INITIAL_DIRECT_SEQUENCE: () => INITIAL_DIRECT_SEQUENCE,
7143
7108
  INITIAL_SEQUENCE: () => INITIAL_SEQUENCE,
7144
7109
  LOGGER_NAMES: () => LOGGER_NAMES,
7145
7110
  Network: () => Network2,
@@ -7162,21 +7127,24 @@ __export(utils_exports, {
7162
7127
  constructFeeBumpTx: () => constructFeeBumpTx,
7163
7128
  constructUnilateralExitFeeBumpPackages: () => constructUnilateralExitFeeBumpPackages,
7164
7129
  constructUnilateralExitTxs: () => constructUnilateralExitTxs,
7165
- createConnectorRefundTransactions: () => createConnectorRefundTransactions,
7166
- createLeafNodeTx: () => createLeafNodeTx,
7167
- createNodeTx: () => createNodeTx,
7168
- createNodeTxs: () => createNodeTxs,
7169
- createRefundTx: () => createRefundTx,
7170
- createRefundTxs: () => createRefundTxs,
7171
- createRootTx: () => createRootTx,
7130
+ createConnectorRefundTxs: () => createConnectorRefundTxs,
7131
+ createCurrentTimelockRefundTxs: () => createCurrentTimelockRefundTxs,
7132
+ createDecrementedTimelockNodeTx: () => createDecrementedTimelockNodeTx,
7133
+ createDecrementedTimelockRefundTxs: () => createDecrementedTimelockRefundTxs,
7134
+ createInitialTimelockNodeTx: () => createInitialTimelockNodeTx,
7135
+ createInitialTimelockRefundTxs: () => createInitialTimelockRefundTxs,
7136
+ createRootNodeTx: () => createRootNodeTx,
7172
7137
  createSigningCommitment: () => createSigningCommitment,
7173
7138
  createSigningNonce: () => createSigningNonce,
7174
- createSplitTx: () => createSplitTx,
7139
+ createTestUnilateralRefundTxs: () => createTestUnilateralRefundTxs,
7140
+ createTestUnilateralTimelockNodeTx: () => createTestUnilateralTimelockNodeTx,
7141
+ createZeroTimelockNodeTx: () => createZeroTimelockNodeTx,
7175
7142
  decodeBech32mTokenIdentifier: () => decodeBech32mTokenIdentifier,
7176
7143
  decodeBytesToSigningCommitment: () => decodeBytesToSigningCommitment,
7177
7144
  decodeBytesToSigningNonce: () => decodeBytesToSigningNonce,
7178
7145
  decodeSparkAddress: () => decodeSparkAddress,
7179
7146
  doesLeafNeedRefresh: () => doesLeafNeedRefresh,
7147
+ doesTxnNeedRenewed: () => doesTxnNeedRenewed,
7180
7148
  encodeBech32mTokenIdentifier: () => encodeBech32mTokenIdentifier,
7181
7149
  encodeSigningCommitmentToBytes: () => encodeSigningCommitmentToBytes,
7182
7150
  encodeSigningNonceToBytes: () => encodeSigningNonceToBytes,
@@ -7220,6 +7188,7 @@ __export(utils_exports, {
7220
7188
  isTxBroadcast: () => isTxBroadcast,
7221
7189
  isValidPublicKey: () => isValidPublicKey,
7222
7190
  isValidSparkAddress: () => isValidSparkAddress,
7191
+ isZeroTimelock: () => isZeroTimelock,
7223
7192
  lastKeyWithTarget: () => lastKeyWithTarget,
7224
7193
  maybeApplyFee: () => maybeApplyFee,
7225
7194
  modInverse: () => modInverse,
@@ -7246,11 +7215,11 @@ import {
7246
7215
  bytesToHex as bytesToHex11,
7247
7216
  bytesToNumberBE as bytesToNumberBE8,
7248
7217
  equalBytes as equalBytes6,
7249
- hexToBytes as hexToBytes13
7218
+ hexToBytes as hexToBytes12
7250
7219
  } from "@noble/curves/utils";
7251
7220
  import { validateMnemonic } from "@scure/bip39";
7252
7221
  import { wordlist as wordlist2 } from "@scure/bip39/wordlists/english";
7253
- import { Address as Address4, OutScript as OutScript3, Transaction as Transaction9 } from "@scure/btc-signer";
7222
+ import { Address as Address4, OutScript as OutScript3, Transaction as Transaction7 } from "@scure/btc-signer";
7254
7223
  import { Mutex } from "async-mutex";
7255
7224
  import { uuidv7 as uuidv74 } from "uuidv7";
7256
7225
 
@@ -8372,28 +8341,14 @@ var SparkAuthProvider = class {
8372
8341
  };
8373
8342
 
8374
8343
  // src/services/coop-exit.ts
8375
- import { Transaction as Transaction7 } from "@scure/btc-signer";
8376
8344
  import { uuidv7 as uuidv72 } from "uuidv7";
8377
8345
 
8378
8346
  // src/services/transfer.ts
8379
8347
  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";
8348
+ import { bytesToHex as bytesToHex10, equalBytes as equalBytes5, numberToBytesBE as numberToBytesBE4 } from "@noble/curves/utils";
8386
8349
  import { sha256 as sha25611 } from "@noble/hashes/sha2";
8387
- import { Transaction as Transaction6 } from "@scure/btc-signer";
8388
8350
  import * as ecies2 from "eciesjs";
8389
8351
  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
8352
  var BaseTransferService = class {
8398
8353
  config;
8399
8354
  connectionManager;
@@ -9075,42 +9030,27 @@ var TransferService = class extends BaseTransferService {
9075
9030
  throw new Error(`Leaf data not found for leaf ${leaf.leaf.id}`);
9076
9031
  }
9077
9032
  const nodeTx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
9078
- const cpfpNodeOutPoint = {
9079
- txid: hexToBytes9(getTxId(nodeTx)),
9080
- index: 0
9081
- };
9082
9033
  let directNodeTx;
9083
- let directNodeOutPoint;
9084
9034
  if (leaf.leaf.directTx.length > 0) {
9085
9035
  directNodeTx = getTxFromRawTxBytes(leaf.leaf.directTx);
9086
- directNodeOutPoint = {
9087
- txid: hexToBytes9(getTxId(directNodeTx)),
9088
- index: 0
9089
- };
9090
9036
  }
9091
9037
  const currRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
9092
- const sequence = currRefundTx.getInput(0).sequence;
9093
- if (!sequence) {
9038
+ const currentSequence = currRefundTx.getInput(0).sequence;
9039
+ if (!currentSequence) {
9094
9040
  throw new ValidationError("Invalid refund transaction", {
9095
9041
  field: "sequence",
9096
9042
  value: currRefundTx.getInput(0),
9097
9043
  expected: "Non-null sequence"
9098
9044
  });
9099
9045
  }
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,
9046
+ const refundTxsParams = {
9047
+ nodeTx,
9048
+ directNodeTx,
9049
+ sequence: currentSequence,
9111
9050
  receivingPubkey: refundSigningData.receivingPubkey,
9112
9051
  network: this.config.getNetwork()
9113
- });
9052
+ };
9053
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = isForClaim ? createCurrentTimelockRefundTxs(refundTxsParams) : createDecrementedTimelockRefundTxs(refundTxsParams);
9114
9054
  refundSigningData.refundTx = cpfpRefundTx;
9115
9055
  refundSigningData.directRefundTx = directRefundTx;
9116
9056
  refundSigningData.directFromCpfpRefundTx = directFromCpfpRefundTx;
@@ -9323,71 +9263,127 @@ var TransferService = class extends BaseTransferService {
9323
9263
  throw new Error(`Error querying pending transfers by sender: ${error}`);
9324
9264
  }
9325
9265
  }
9326
- async refreshTimelockNodesInternal(node, parentNode, useTestUnilateralSequence) {
9266
+ async renewRefundTxn(node, parentNode) {
9267
+ const sparkClient = await this.connectionManager.createSparkClient(
9268
+ this.config.getCoordinatorAddress()
9269
+ );
9270
+ const signingJobs = await this.createRenewRefundSigningJobs(
9271
+ node,
9272
+ parentNode
9273
+ );
9274
+ const statechainCommitments = await sparkClient.get_signing_commitments({
9275
+ nodeIds: [node.id],
9276
+ count: signingJobs.length
9277
+ });
9278
+ const mappedSigningJobs = signingJobs.map((signingJob, index) => {
9279
+ const signingNonceCommitments = statechainCommitments.signingCommitments[index]?.signingNonceCommitments;
9280
+ if (!signingNonceCommitments) {
9281
+ throw new Error("Signing nonce commitments not found");
9282
+ }
9283
+ return {
9284
+ ...signingJob,
9285
+ signingNonceCommitments
9286
+ };
9287
+ });
9288
+ const userSignedTxSigningJobs = await this.signingService.signSigningJobs(mappedSigningJobs);
9289
+ const renewRefundTimelockSigningJob = {
9290
+ nodeTxSigningJob: userSignedTxSigningJobs.get("node"),
9291
+ refundTxSigningJob: userSignedTxSigningJobs.get("cpfp"),
9292
+ directNodeTxSigningJob: userSignedTxSigningJobs.get("directNode"),
9293
+ directRefundTxSigningJob: userSignedTxSigningJobs.get("direct"),
9294
+ directFromCpfpRefundTxSigningJob: userSignedTxSigningJobs.get("directFromCpfp")
9295
+ };
9296
+ const response = await sparkClient.renew_leaf({
9297
+ leafId: node.id,
9298
+ signingJobs: {
9299
+ $case: "renewRefundTimelockSigningJob",
9300
+ renewRefundTimelockSigningJob
9301
+ }
9302
+ });
9303
+ if (response.renewResult?.$case !== "renewRefundTimelockResult" || !response.renewResult?.renewRefundTimelockResult.node) {
9304
+ throw new ValidationError("Unexpected renew result", {
9305
+ field: "renewResult",
9306
+ value: response.renewResult
9307
+ });
9308
+ }
9309
+ return response.renewResult?.renewRefundTimelockResult.node;
9310
+ }
9311
+ async renewNodeTxn(node, parentNode) {
9312
+ const sparkClient = await this.connectionManager.createSparkClient(
9313
+ this.config.getCoordinatorAddress()
9314
+ );
9315
+ const signingJobs = await this.createRenewNodeSigningJobs(node, parentNode);
9316
+ const statechainCommitments = await sparkClient.get_signing_commitments({
9317
+ nodeIds: [node.id],
9318
+ count: signingJobs.length
9319
+ });
9320
+ const mappedSigningJobs = signingJobs.map((signingJob, index) => {
9321
+ const signingNonceCommitments = statechainCommitments.signingCommitments[index]?.signingNonceCommitments;
9322
+ if (!signingNonceCommitments) {
9323
+ throw new Error("Signing nonce commitments not found");
9324
+ }
9325
+ return {
9326
+ ...signingJob,
9327
+ signingNonceCommitments
9328
+ };
9329
+ });
9330
+ const userSignedTxSigningJobs = await this.signingService.signSigningJobs(mappedSigningJobs);
9331
+ const response = await sparkClient.renew_leaf({
9332
+ leafId: node.id,
9333
+ signingJobs: {
9334
+ $case: "renewNodeTimelockSigningJob",
9335
+ renewNodeTimelockSigningJob: {
9336
+ splitNodeTxSigningJob: userSignedTxSigningJobs.get("split"),
9337
+ splitNodeDirectTxSigningJob: userSignedTxSigningJobs.get("directSplit"),
9338
+ nodeTxSigningJob: userSignedTxSigningJobs.get("node"),
9339
+ directNodeTxSigningJob: userSignedTxSigningJobs.get("directNode"),
9340
+ refundTxSigningJob: userSignedTxSigningJobs.get("cpfp"),
9341
+ directRefundTxSigningJob: userSignedTxSigningJobs.get("direct"),
9342
+ directFromCpfpRefundTxSigningJob: userSignedTxSigningJobs.get("directFromCpfp")
9343
+ }
9344
+ }
9345
+ });
9346
+ if (response.renewResult?.$case !== "renewNodeTimelockResult" || !response.renewResult?.renewNodeTimelockResult.node) {
9347
+ throw new ValidationError("Unexpected renew result", {
9348
+ field: "renewResult",
9349
+ value: response.renewResult
9350
+ });
9351
+ }
9352
+ return response.renewResult.renewNodeTimelockResult.node;
9353
+ }
9354
+ async createRenewRefundSigningJobs(node, parentNode) {
9327
9355
  const signingJobs = [];
9328
- const parentNodeTx = getTxFromRawTxBytes(parentNode.nodeTx);
9329
- const parentNodeOutput = parentNodeTx.getOutput(0);
9356
+ const parentTx = getTxFromRawTxBytes(parentNode.nodeTx);
9357
+ const parentNodeOutput = getTxFromRawTxBytes(parentNode.nodeTx).getOutput(
9358
+ 0
9359
+ );
9330
9360
  if (!parentNodeOutput) {
9331
- throw Error("Could not get parent node output");
9361
+ throw new Error("Parent node output not found");
9332
9362
  }
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 = {
9363
+ const unsignedParentNodeOutput = {
9358
9364
  script: parentNodeOutput.script,
9359
9365
  amount: parentNodeOutput.amount
9360
9366
  };
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({
9367
+ const keyDerivation = {
9382
9368
  type: "leaf" /* LEAF */,
9383
9369
  path: node.id
9384
- });
9370
+ };
9371
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
9372
+ const nodeTx = getTxFromRawTxBytes(node.nodeTx);
9373
+ const refundTx = getTxFromRawTxBytes(node.refundTx);
9374
+ const { nodeTx: newNodeTx, directNodeTx: newDirectNodeTx } = createDecrementedTimelockNodeTx(parentTx, nodeTx);
9385
9375
  signingJobs.push({
9386
9376
  signingPublicKey,
9387
- rawTx: cpfpNodeTx.toBytes(),
9377
+ rawTx: newNodeTx.toBytes(),
9388
9378
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9389
9379
  type: "node",
9390
- parentTxOut: parentNodeOutput
9380
+ parentTxOut: unsignedParentNodeOutput,
9381
+ leafId: node.id,
9382
+ keyDerivation: {
9383
+ type: "leaf" /* LEAF */,
9384
+ path: node.id
9385
+ },
9386
+ verifyingKey: node.verifyingPublicKey
9391
9387
  });
9392
9388
  if (newDirectNodeTx) {
9393
9389
  signingJobs.push({
@@ -9395,537 +9391,299 @@ var TransferService = class extends BaseTransferService {
9395
9391
  rawTx: newDirectNodeTx.toBytes(),
9396
9392
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9397
9393
  type: "directNode",
9398
- parentTxOut: parentNodeOutput
9394
+ parentTxOut: unsignedParentNodeOutput,
9395
+ leafId: node.id,
9396
+ keyDerivation: {
9397
+ type: "leaf" /* LEAF */,
9398
+ path: node.id
9399
+ },
9400
+ verifyingKey: node.verifyingPublicKey
9399
9401
  });
9400
9402
  }
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
- };
9411
- }
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
- }),
9422
- network: this.config.getNetwork()
9423
- });
9424
- signingJobs.push({
9425
- signingPublicKey,
9426
- rawTx: cpfpRefundTx.toBytes(),
9427
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9428
- type: "cpfp",
9429
- parentTxOut: newCpfpNodeOutput
9430
- });
9431
- if (directRefundTx && newDirectNodeOutput) {
9432
- signingJobs.push({
9433
- signingPublicKey,
9434
- rawTx: directRefundTx.toBytes(),
9435
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9436
- type: "direct",
9437
- parentTxOut: newDirectNodeOutput
9438
- });
9439
- }
9440
- if (directFromCpfpRefundTx && newCpfpNodeOutput) {
9441
- signingJobs.push({
9442
- signingPublicKey,
9443
- rawTx: directFromCpfpRefundTx.toBytes(),
9444
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9445
- type: "directFromCpfp",
9446
- parentTxOut: newCpfpNodeOutput
9447
- });
9448
- }
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);
9530
- }
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
- };
9403
+ const newCpfpNodeOutput = newNodeTx.getOutput(0);
9404
+ if (!newCpfpNodeOutput) {
9405
+ throw Error("Could not get new cpfp node output");
9582
9406
  }
9407
+ const newDirectNodeOutput = newDirectNodeTx?.getOutput(0);
9583
9408
  const amountSats = refundTx.getOutput(0).amount;
9584
9409
  if (amountSats === void 0) {
9585
9410
  throw new Error("Amount not found in extendTimelock");
9586
9411
  }
9587
- const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation({
9588
- type: "leaf" /* LEAF */,
9589
- path: node.id
9590
- });
9412
+ const directAmountSats = newDirectNodeOutput?.amount;
9413
+ if (directAmountSats === void 0) {
9414
+ throw new Error("Amount not found in extendTimelock");
9415
+ }
9591
9416
  const {
9592
- cpfpRefundTx: newCpfpRefundTx,
9417
+ cpfpRefundTx: newRefundTx,
9593
9418
  directRefundTx: newDirectRefundTx,
9594
9419
  directFromCpfpRefundTx: newDirectFromCpfpRefundTx
9595
- } = createRefundTxs({
9596
- sequence: INITIAL_SEQUENCE,
9597
- directSequence: INITIAL_DIRECT_SEQUENCE,
9598
- input: newCpfpRefundOutPoint,
9599
- directInput: newDirectRefundOutPoint,
9600
- amountSats,
9420
+ } = createInitialTimelockRefundTxs({
9421
+ nodeTx: newNodeTx,
9422
+ directNodeTx: newDirectNodeTx,
9601
9423
  receivingPubkey: signingPublicKey,
9602
9424
  network: this.config.getNetwork()
9603
9425
  });
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)
9615
- );
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()
9632
- };
9633
- const newDirectRefundSigningJob = newDirectRefundTx ? {
9634
- signingPublicKey,
9635
- rawTx: newDirectRefundTx.toBytes(),
9636
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
9637
- } : void 0;
9638
- const newDirectFromCpfpRefundSigningJob = newDirectFromCpfpRefundTx ? {
9426
+ signingJobs.push({
9639
9427
  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({
9428
+ rawTx: newRefundTx.toBytes(),
9429
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9430
+ type: "cpfp",
9431
+ parentTxOut: newCpfpNodeOutput,
9647
9432
  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
9433
+ keyDerivation,
9434
+ verifyingKey: node.verifyingPublicKey
9654
9435
  });
9655
- if (!response.nodeTxSigningResult || !response.refundTxSigningResult) {
9656
- throw new Error("Signing result does not exist");
9436
+ if (newDirectRefundTx && newDirectNodeOutput) {
9437
+ signingJobs.push({
9438
+ signingPublicKey,
9439
+ rawTx: newDirectRefundTx.toBytes(),
9440
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9441
+ type: "direct",
9442
+ parentTxOut: newDirectNodeOutput,
9443
+ leafId: node.id,
9444
+ keyDerivation,
9445
+ verifyingKey: node.verifyingPublicKey
9446
+ });
9447
+ }
9448
+ if (newDirectFromCpfpRefundTx && newDirectNodeOutput) {
9449
+ signingJobs.push({
9450
+ signingPublicKey,
9451
+ rawTx: newDirectFromCpfpRefundTx.toBytes(),
9452
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9453
+ type: "directFromCpfp",
9454
+ parentTxOut: newCpfpNodeOutput,
9455
+ leafId: node.id,
9456
+ keyDerivation,
9457
+ verifyingKey: node.verifyingPublicKey
9458
+ });
9657
9459
  }
9460
+ return signingJobs;
9461
+ }
9462
+ async createRenewNodeSigningJobs(node, parentNode) {
9463
+ const signingJobs = [];
9464
+ const parentTx = getTxFromRawTxBytes(parentNode.nodeTx);
9465
+ const parentNodeOutput = getTxFromRawTxBytes(parentNode.nodeTx).getOutput(
9466
+ 0
9467
+ );
9468
+ const unsignedParentNodeOutput = {
9469
+ script: parentNodeOutput.script,
9470
+ amount: parentNodeOutput.amount
9471
+ };
9658
9472
  const keyDerivation = {
9659
9473
  type: "leaf" /* LEAF */,
9660
9474
  path: node.id
9661
9475
  };
9662
- const nodeUserSig = await this.config.signer.signFrost({
9663
- message: nodeSighash,
9476
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
9477
+ const { nodeTx: splitNodeTx, directNodeTx: splitNodeDirectTx } = createZeroTimelockNodeTx(parentTx);
9478
+ signingJobs.push({
9479
+ signingPublicKey,
9480
+ rawTx: splitNodeTx.toBytes(),
9481
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9482
+ type: "split",
9483
+ parentTxOut: unsignedParentNodeOutput,
9484
+ leafId: node.id,
9664
9485
  keyDerivation,
9665
- publicKey: signingPublicKey,
9666
- verifyingKey: response.nodeTxSigningResult.verifyingKey,
9667
- selfCommitment: newNodeSigningJob.signingNonceCommitment,
9668
- statechainCommitments: response.nodeTxSigningResult.signingResult?.signingNonceCommitments,
9669
- adaptorPubKey: new Uint8Array()
9670
- });
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()
9486
+ verifyingKey: node.verifyingPublicKey
9681
9487
  });
9682
- let directNodeSig;
9683
- if (directNodeSighash && newDirectNodeSigningJob && response.directNodeTxSigningResult) {
9684
- const directNodeUserSig = await this.config.signer.signFrost({
9685
- message: directNodeSighash,
9488
+ if (splitNodeDirectTx) {
9489
+ signingJobs.push({
9490
+ signingPublicKey,
9491
+ rawTx: splitNodeDirectTx.toBytes(),
9492
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9493
+ type: "directSplit",
9494
+ parentTxOut: unsignedParentNodeOutput,
9495
+ leafId: node.id,
9686
9496
  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()
9497
+ verifyingKey: node.verifyingPublicKey
9703
9498
  });
9704
9499
  }
9705
- const cpfpRefundUserSig = await this.config.signer.signFrost({
9706
- message: cpfpRefundSighash,
9500
+ const splitNodeOutput = splitNodeTx.getOutput(0);
9501
+ const splitNodeDirectOutput = splitNodeDirectTx.getOutput(0);
9502
+ if (!splitNodeDirectOutput.amount || !splitNodeDirectOutput.script) {
9503
+ throw new Error("Could not get split node output");
9504
+ }
9505
+ const unsignedSplitNodeOutput = {
9506
+ script: splitNodeDirectOutput.script,
9507
+ amount: splitNodeDirectOutput.amount
9508
+ };
9509
+ const { nodeTx: newNodeTx, directNodeTx: newDirectNodeTx } = createInitialTimelockNodeTx(splitNodeTx);
9510
+ signingJobs.push({
9511
+ signingPublicKey,
9512
+ rawTx: newNodeTx.toBytes(),
9513
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9514
+ type: "node",
9515
+ parentTxOut: splitNodeOutput,
9516
+ leafId: node.id,
9707
9517
  keyDerivation,
9708
- publicKey: signingPublicKey,
9709
- verifyingKey: response.refundTxSigningResult.verifyingKey,
9710
- selfCommitment: newCpfpRefundSigningJob.signingNonceCommitment,
9711
- statechainCommitments: response.refundTxSigningResult.signingResult?.signingNonceCommitments,
9712
- adaptorPubKey: new Uint8Array()
9713
- });
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()
9518
+ verifyingKey: node.verifyingPublicKey
9724
9519
  });
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,
9520
+ if (newDirectNodeTx) {
9521
+ signingJobs.push({
9522
+ signingPublicKey,
9523
+ rawTx: newDirectNodeTx.toBytes(),
9524
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9525
+ type: "directNode",
9526
+ parentTxOut: splitNodeOutput,
9527
+ leafId: node.id,
9752
9528
  keyDerivation,
9753
- publicKey: signingPublicKey,
9754
- verifyingKey: response.directFromCpfpRefundTxSigningResult.verifyingKey,
9755
- selfCommitment: newDirectFromCpfpRefundSigningJob.signingNonceCommitment,
9756
- statechainCommitments: response.directFromCpfpRefundTxSigningResult.signingResult?.signingNonceCommitments,
9757
- adaptorPubKey: new Uint8Array()
9529
+ verifyingKey: node.verifyingPublicKey
9758
9530
  });
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()
9769
- });
9770
- }
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
9531
  }
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
- };
9532
+ const newCpfpNodeOutput = newNodeTx.getOutput(0);
9533
+ if (!newCpfpNodeOutput) {
9534
+ throw Error("Could not get new cpfp node output");
9822
9535
  }
9536
+ const newDirectNodeOutput = newDirectNodeTx?.getOutput(0);
9823
9537
  const {
9824
- cpfpRefundTx: newCpfpRefundTx,
9538
+ cpfpRefundTx: newRefundTx,
9825
9539
  directRefundTx: newDirectRefundTx,
9826
9540
  directFromCpfpRefundTx: newDirectFromCpfpRefundTx
9827
- } = createRefundTxs({
9828
- sequence: nextSequence,
9829
- directSequence: nextDirectSequence,
9830
- input: cpfpRefundOutPoint,
9831
- directInput: directRefundOutPoint,
9832
- amountSats: nodeOutput.amount,
9541
+ } = createInitialTimelockRefundTxs({
9542
+ nodeTx: newNodeTx,
9543
+ directNodeTx: newDirectNodeTx,
9833
9544
  receivingPubkey: signingPublicKey,
9834
9545
  network: this.config.getNetwork()
9835
9546
  });
9836
- const signingJobs = [];
9837
9547
  signingJobs.push({
9838
9548
  signingPublicKey,
9839
- rawTx: newCpfpRefundTx.toBytes(),
9549
+ rawTx: newRefundTx.toBytes(),
9840
9550
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9841
9551
  type: "cpfp",
9842
- parentTxOut: nodeOutput
9552
+ parentTxOut: newCpfpNodeOutput,
9553
+ leafId: node.id,
9554
+ keyDerivation,
9555
+ verifyingKey: node.verifyingPublicKey
9843
9556
  });
9844
- const directNodeTxOut = directNodeTx?.getOutput(0);
9845
- if (newDirectRefundTx && directNodeTxOut) {
9557
+ if (newDirectRefundTx && newDirectNodeOutput) {
9846
9558
  signingJobs.push({
9847
9559
  signingPublicKey,
9848
9560
  rawTx: newDirectRefundTx.toBytes(),
9849
9561
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9850
9562
  type: "direct",
9851
- parentTxOut: directNodeTxOut
9563
+ parentTxOut: newDirectNodeOutput,
9564
+ leafId: node.id,
9565
+ keyDerivation,
9566
+ verifyingKey: node.verifyingPublicKey
9852
9567
  });
9853
9568
  }
9854
- if (newDirectFromCpfpRefundTx) {
9569
+ if (newDirectFromCpfpRefundTx && newDirectNodeOutput) {
9855
9570
  signingJobs.push({
9856
9571
  signingPublicKey,
9857
9572
  rawTx: newDirectFromCpfpRefundTx.toBytes(),
9858
9573
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9859
9574
  type: "directFromCpfp",
9860
- parentTxOut: nodeOutput
9575
+ parentTxOut: newCpfpNodeOutput,
9576
+ leafId: node.id,
9577
+ keyDerivation,
9578
+ verifyingKey: node.verifyingPublicKey
9861
9579
  });
9862
9580
  }
9581
+ return signingJobs;
9582
+ }
9583
+ async renewZeroTimelockNodeTxn(node) {
9863
9584
  const sparkClient = await this.connectionManager.createSparkClient(
9864
9585
  this.config.getCoordinatorAddress()
9865
9586
  );
9866
- const response = await sparkClient.refresh_timelock_v2({
9867
- leafId: node.id,
9868
- ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
9869
- signingJobs: signingJobs.map(getSigningJobProto)
9587
+ const signingJobs = await this.createRenewZeroTimelockNodeSigningJobs(node);
9588
+ const statechainCommitments = await sparkClient.get_signing_commitments({
9589
+ nodeIds: [node.id],
9590
+ count: signingJobs.length
9591
+ });
9592
+ const mappedSigningJobs = signingJobs.map((signingJob, index) => {
9593
+ const signingNonceCommitments = statechainCommitments.signingCommitments[index]?.signingNonceCommitments;
9594
+ if (!signingNonceCommitments) {
9595
+ throw new ValidationError("Signing nonce commitments not found", {
9596
+ field: "signingNonceCommitments",
9597
+ value: signingNonceCommitments
9598
+ });
9599
+ }
9600
+ return {
9601
+ ...signingJob,
9602
+ signingNonceCommitments
9603
+ };
9870
9604
  });
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");
9605
+ const userSignedTxSigningJobs = await this.signingService.signSigningJobs(mappedSigningJobs);
9606
+ const renewZeroTimelockNodeSigningJob = {
9607
+ nodeTxSigningJob: userSignedTxSigningJobs.get("node"),
9608
+ refundTxSigningJob: userSignedTxSigningJobs.get("cpfp"),
9609
+ directNodeTxSigningJob: userSignedTxSigningJobs.get("directNode"),
9610
+ directRefundTxSigningJob: void 0,
9611
+ directFromCpfpRefundTxSigningJob: userSignedTxSigningJobs.get("directFromCpfp")
9612
+ };
9613
+ const response = await sparkClient.renew_leaf({
9614
+ leafId: node.id,
9615
+ signingJobs: {
9616
+ $case: "renewNodeZeroTimelockSigningJob",
9617
+ renewNodeZeroTimelockSigningJob: renewZeroTimelockNodeSigningJob
9883
9618
  }
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()
9619
+ });
9620
+ if (response.renewResult?.$case !== "renewNodeZeroTimelockResult" || !response.renewResult?.renewNodeZeroTimelockResult.node) {
9621
+ throw new ValidationError("Unexpected renew result", {
9622
+ field: "renewResult",
9623
+ value: response.renewResult
9906
9624
  });
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
9625
  }
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
- ]
9626
+ return response.renewResult.renewNodeZeroTimelockResult.node;
9627
+ }
9628
+ async createRenewZeroTimelockNodeSigningJobs(node) {
9629
+ const signingJobs = [];
9630
+ const keyDerivation = {
9631
+ type: "leaf" /* LEAF */,
9632
+ path: node.id
9633
+ };
9634
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
9635
+ const nodeTx = getTxFromRawTxBytes(node.nodeTx);
9636
+ const { nodeTx: newNodeTx, directNodeTx: newDirectNodeTx } = createZeroTimelockNodeTx(nodeTx);
9637
+ signingJobs.push({
9638
+ signingPublicKey,
9639
+ rawTx: newNodeTx.toBytes(),
9640
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9641
+ type: "node",
9642
+ parentTxOut: nodeTx.getOutput(0),
9643
+ leafId: node.id,
9644
+ keyDerivation,
9645
+ verifyingKey: node.verifyingPublicKey
9927
9646
  });
9928
- return result;
9647
+ signingJobs.push({
9648
+ signingPublicKey,
9649
+ rawTx: newDirectNodeTx.toBytes(),
9650
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9651
+ type: "directNode",
9652
+ parentTxOut: nodeTx.getOutput(0),
9653
+ leafId: node.id,
9654
+ keyDerivation,
9655
+ verifyingKey: node.verifyingPublicKey
9656
+ });
9657
+ const { cpfpRefundTx, directFromCpfpRefundTx } = createInitialTimelockRefundTxs({
9658
+ nodeTx: newNodeTx,
9659
+ directNodeTx: newDirectNodeTx,
9660
+ receivingPubkey: signingPublicKey,
9661
+ network: this.config.getNetwork()
9662
+ });
9663
+ signingJobs.push({
9664
+ signingPublicKey,
9665
+ rawTx: cpfpRefundTx.toBytes(),
9666
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9667
+ type: "cpfp",
9668
+ parentTxOut: newNodeTx.getOutput(0),
9669
+ leafId: node.id,
9670
+ keyDerivation,
9671
+ verifyingKey: node.verifyingPublicKey
9672
+ });
9673
+ if (!directFromCpfpRefundTx) {
9674
+ throw new Error("Could not create direct refund transactions");
9675
+ }
9676
+ signingJobs.push({
9677
+ signingPublicKey,
9678
+ rawTx: directFromCpfpRefundTx.toBytes(),
9679
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9680
+ type: "directFromCpfp",
9681
+ parentTxOut: newNodeTx.getOutput(0),
9682
+ leafId: node.id,
9683
+ keyDerivation,
9684
+ verifyingKey: node.verifyingPublicKey
9685
+ });
9686
+ return signingJobs;
9929
9687
  }
9930
9688
  };
9931
9689
 
@@ -9967,71 +9725,6 @@ var CoopExitService = class extends BaseTransferService {
9967
9725
  directFromCpfpSignaturesMap
9968
9726
  };
9969
9727
  }
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
9728
  async signCoopExitRefunds(leaves, exitTxId, connectorOutputs, receiverPubKey, transferId) {
10036
9729
  if (leaves.length !== connectorOutputs.length) {
10037
9730
  throw new ValidationError(
@@ -10065,29 +9758,39 @@ var CoopExitService = class extends BaseTransferService {
10065
9758
  expected: "Valid connector output"
10066
9759
  });
10067
9760
  }
9761
+ const nodeTx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
9762
+ let directNodeTx;
9763
+ if (leaf.leaf.directTx.length > 0) {
9764
+ directNodeTx = getTxFromRawTxBytes(leaf.leaf.directTx);
9765
+ }
10068
9766
  const currentRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
10069
- const sequence = currentRefundTx.getInput(0).sequence;
10070
- if (!sequence) {
9767
+ if (!currentRefundTx) {
9768
+ throw new ValidationError("Invalid refund transaction", {
9769
+ field: "currentRefundTx",
9770
+ value: currentRefundTx,
9771
+ expected: "Non-null refund transaction"
9772
+ });
9773
+ }
9774
+ const currentSequence = currentRefundTx.getInput(0).sequence;
9775
+ if (!currentSequence) {
10071
9776
  throw new ValidationError("Invalid refund transaction", {
10072
9777
  field: "sequence",
10073
9778
  value: currentRefundTx.getInput(0),
10074
9779
  expected: "Non-null sequence"
10075
9780
  });
10076
9781
  }
10077
- const { nextSequence, nextDirectSequence } = getNextTransactionSequence(sequence);
10078
9782
  let currentDirectRefundTx;
10079
9783
  if (leaf.leaf.directRefundTx.length > 0) {
10080
9784
  currentDirectRefundTx = getTxFromRawTxBytes(leaf.leaf.directRefundTx);
10081
9785
  }
10082
- const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = this.createConnectorRefundTransactions(
10083
- nextSequence,
10084
- nextDirectSequence,
10085
- currentRefundTx.getInput(0),
10086
- currentDirectRefundTx?.getInput(0),
9786
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createConnectorRefundTxs({
9787
+ nodeTx,
9788
+ directNodeTx,
9789
+ sequence: currentSequence,
10087
9790
  connectorOutput,
10088
- BigInt(leaf.leaf.value),
10089
- receiverPubKey
10090
- );
9791
+ receivingPubkey: receiverPubKey,
9792
+ network: this.config.getNetwork()
9793
+ });
10091
9794
  const signingNonceCommitment = await this.config.signer.getRandomSigningCommitment();
10092
9795
  const directSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
10093
9796
  const directFromCpfpSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
@@ -10568,12 +10271,9 @@ import {
10568
10271
  import { EventEmitter } from "eventemitter3";
10569
10272
  import { ClientError, Status } from "nice-grpc-common";
10570
10273
 
10571
- // src/services/signing.ts
10572
- import { hexToBytes as hexToBytes12 } from "@noble/curves/utils";
10573
-
10574
10274
  // src/utils/htlc-transactions.ts
10575
10275
  import {
10576
- Transaction as Transaction8,
10276
+ Transaction as Transaction6,
10577
10277
  Script,
10578
10278
  taprootListToTree,
10579
10279
  p2tr as p2tr3,
@@ -10661,7 +10361,7 @@ function createLightningHTLCTransaction({
10661
10361
  txid: hexToBytes11(getTxId(nodeTx)),
10662
10362
  index: 0
10663
10363
  };
10664
- const htlcTransaction = new Transaction8({
10364
+ const htlcTransaction = new Transaction6({
10665
10365
  version: 3,
10666
10366
  allowUnknownOutputs: true
10667
10367
  });
@@ -10780,20 +10480,7 @@ var SigningService = class {
10780
10480
  });
10781
10481
  }
10782
10482
  const nodeTx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
10783
- const cpfpNodeOutPoint = {
10784
- txid: hexToBytes12(getTxId(nodeTx)),
10785
- index: 0
10786
- };
10787
10483
  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
10484
  const amountSats = currRefundTx.getOutput(0).amount;
10798
10485
  if (amountSats === void 0) {
10799
10486
  throw new ValidationError("Invalid refund transaction", {
@@ -10803,20 +10490,21 @@ var SigningService = class {
10803
10490
  });
10804
10491
  }
10805
10492
  let directNodeTx;
10806
- let directNodeOutPoint;
10807
10493
  if (leaf.leaf.directTx.length > 0) {
10808
10494
  directNodeTx = getTxFromRawTxBytes(leaf.leaf.directTx);
10809
- directNodeOutPoint = {
10810
- txid: hexToBytes12(getTxId(directNodeTx)),
10811
- index: 0
10812
- };
10813
10495
  }
10814
- const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
10815
- sequence: nextSequence,
10816
- directSequence: nextDirectSequence,
10817
- input: cpfpNodeOutPoint,
10818
- directInput: directNodeOutPoint,
10819
- amountSats,
10496
+ const currentSequence = currRefundTx.getInput(0).sequence;
10497
+ if (!currentSequence) {
10498
+ throw new ValidationError("Invalid refund transaction", {
10499
+ field: "sequence",
10500
+ value: currRefundTx.getInput(0),
10501
+ expected: "Non-null sequence"
10502
+ });
10503
+ }
10504
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createDecrementedTimelockRefundTxs({
10505
+ nodeTx,
10506
+ directNodeTx,
10507
+ sequence: currentSequence,
10820
10508
  receivingPubkey: receiverIdentityPubkey,
10821
10509
  network: this.config.getNetwork()
10822
10510
  });
@@ -10832,7 +10520,8 @@ var SigningService = class {
10832
10520
  cpfpSigningCommitments[i]?.signingNonceCommitments
10833
10521
  );
10834
10522
  cpfpLeafSigningJobs.push(...signingJobs);
10835
- if (directRefundTx) {
10523
+ const isZeroNode = getCurrentTimelock(nodeTx.getInput(0).sequence);
10524
+ if (directRefundTx && !isZeroNode) {
10836
10525
  if (!directNodeTx) {
10837
10526
  throw new ValidationError(
10838
10527
  "Direct node transaction undefined while direct refund transaction is defined",
@@ -10857,16 +10546,6 @@ var SigningService = class {
10857
10546
  directLeafSigningJobs.push(...signingJobs2);
10858
10547
  }
10859
10548
  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
10549
  const refundSighash2 = getSigHashFromTx(
10871
10550
  directFromCpfpRefundTx,
10872
10551
  0,
@@ -10993,6 +10672,35 @@ var SigningService = class {
10993
10672
  directFromCpfpLeafSigningJobs
10994
10673
  };
10995
10674
  }
10675
+ async signSigningJobs(signingJobs) {
10676
+ const userSignedTxSigningJobs = /* @__PURE__ */ new Map();
10677
+ for (const signingJob of signingJobs) {
10678
+ const rawTx = getTxFromRawTxBytes(signingJob.rawTx);
10679
+ const txOut = signingJob.parentTxOut;
10680
+ const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
10681
+ const userSignature = await this.config.signer.signFrost({
10682
+ message: rawTxSighash,
10683
+ keyDerivation: signingJob.keyDerivation,
10684
+ publicKey: signingJob.signingPublicKey,
10685
+ verifyingKey: signingJob.verifyingKey,
10686
+ selfCommitment: signingJob.signingNonceCommitment,
10687
+ statechainCommitments: signingJob.signingNonceCommitments,
10688
+ adaptorPubKey: new Uint8Array()
10689
+ });
10690
+ const userSignedTxSigningJob = {
10691
+ leafId: signingJob.leafId,
10692
+ signingPublicKey: signingJob.signingPublicKey,
10693
+ rawTx: rawTx.toBytes(),
10694
+ signingNonceCommitment: signingJob.signingNonceCommitment.commitment,
10695
+ signingCommitments: {
10696
+ signingCommitments: signingJob.signingNonceCommitments
10697
+ },
10698
+ userSignature
10699
+ };
10700
+ userSignedTxSigningJobs.set(signingJob.type, userSignedTxSigningJob);
10701
+ }
10702
+ return userSignedTxSigningJobs;
10703
+ }
10996
10704
  };
10997
10705
 
10998
10706
  // src/utils/chunkArray.ts
@@ -11249,10 +10957,8 @@ var SparkWallet = class extends EventEmitter {
11249
10957
  }
11250
10958
  } else if (isDepositStreamEvent(event)) {
11251
10959
  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
10960
+ await this.withLeaves(async () => {
10961
+ this.leaves.push(deposit);
11256
10962
  });
11257
10963
  this.emit(
11258
10964
  SparkWalletEvent.DepositConfirmed,
@@ -11428,19 +11134,6 @@ var SparkWallet = class extends EventEmitter {
11428
11134
  }
11429
11135
  return availableLeaves.filter(([_, node]) => !leavesToIgnore.has(node.id)).map(([_, node]) => node);
11430
11136
  }
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
11137
  verifyKey(pubkey1, pubkey2, verifyingKey) {
11445
11138
  return equalBytes6(addPublicKeys(pubkey1, pubkey2), verifyingKey);
11446
11139
  }
@@ -11612,10 +11305,8 @@ var SparkWallet = class extends EventEmitter {
11612
11305
  async syncWallet() {
11613
11306
  await this.syncTokenOutputs();
11614
11307
  let leaves = await this.getLeaves();
11615
- leaves = await this.checkRefreshTimelockNodes(leaves);
11616
- leaves = await this.checkExtendTimeLockNodes(leaves);
11308
+ leaves = await this.checkRenewLeaves(leaves);
11617
11309
  this.leaves = leaves;
11618
- this.checkExtendLeaves(leaves);
11619
11310
  this.optimizeLeaves().catch((e) => {
11620
11311
  console.error("Failed to optimize leaves", e);
11621
11312
  });
@@ -11735,7 +11426,7 @@ var SparkWallet = class extends EventEmitter {
11735
11426
  mnemonic = mnemonicOrSeed;
11736
11427
  seed = await this.config.signer.mnemonicToSeed(mnemonicOrSeed);
11737
11428
  } else {
11738
- seed = hexToBytes13(mnemonicOrSeed);
11429
+ seed = hexToBytes12(mnemonicOrSeed);
11739
11430
  }
11740
11431
  }
11741
11432
  await this.initWalletFromSeed(seed, accountNumber);
@@ -11878,7 +11569,7 @@ var SparkWallet = class extends EventEmitter {
11878
11569
  directFromCpfpSignatureMap
11879
11570
  } = await this.transferService.startSwapSignRefund(
11880
11571
  leafKeyTweaks,
11881
- hexToBytes13(this.config.getSspIdentityPublicKey()),
11572
+ hexToBytes12(this.config.getSspIdentityPublicKey()),
11882
11573
  new Date(Date.now() + 2 * 60 * 1e3)
11883
11574
  );
11884
11575
  try {
@@ -12093,7 +11784,7 @@ var SparkWallet = class extends EventEmitter {
12093
11784
  throw new Error(`Leaf not found for node ${nodeId}`);
12094
11785
  }
12095
11786
  const cpfpNodeTx = getTxFromRawTxBytes(node.nodeTx);
12096
- const cpfpRefundTxBytes = hexToBytes13(leaf.rawUnsignedRefundTransaction);
11787
+ const cpfpRefundTxBytes = hexToBytes12(leaf.rawUnsignedRefundTransaction);
12097
11788
  const cpfpRefundTx = getTxFromRawTxBytes(cpfpRefundTxBytes);
12098
11789
  const cpfpSighash = getSigHashFromTx(
12099
11790
  cpfpRefundTx,
@@ -12102,7 +11793,7 @@ var SparkWallet = class extends EventEmitter {
12102
11793
  );
12103
11794
  const nodePublicKey = node.verifyingPublicKey;
12104
11795
  const taprootKey = computeTaprootKeyNoScript(nodePublicKey.slice(1));
12105
- const cpfpAdaptorSignatureBytes = hexToBytes13(
11796
+ const cpfpAdaptorSignatureBytes = hexToBytes12(
12106
11797
  leaf.adaptorSignedSignature
12107
11798
  );
12108
11799
  applyAdaptorToSignature(
@@ -12113,7 +11804,7 @@ var SparkWallet = class extends EventEmitter {
12113
11804
  );
12114
11805
  if (leaf.directRawUnsignedRefundTransaction) {
12115
11806
  const directNodeTx = getTxFromRawTxBytes(node.directTx);
12116
- const directRefundTxBytes = hexToBytes13(
11807
+ const directRefundTxBytes = hexToBytes12(
12117
11808
  leaf.directRawUnsignedRefundTransaction
12118
11809
  );
12119
11810
  const directRefundTx = getTxFromRawTxBytes(directRefundTxBytes);
@@ -12127,7 +11818,7 @@ var SparkWallet = class extends EventEmitter {
12127
11818
  `Direct adaptor signed signature missing for node ${nodeId}`
12128
11819
  );
12129
11820
  }
12130
- const directAdaptorSignatureBytes = hexToBytes13(
11821
+ const directAdaptorSignatureBytes = hexToBytes12(
12131
11822
  leaf.directAdaptorSignedSignature
12132
11823
  );
12133
11824
  applyAdaptorToSignature(
@@ -12138,7 +11829,7 @@ var SparkWallet = class extends EventEmitter {
12138
11829
  );
12139
11830
  }
12140
11831
  if (leaf.directFromCpfpRawUnsignedRefundTransaction) {
12141
- const directFromCpfpRefundTxBytes = hexToBytes13(
11832
+ const directFromCpfpRefundTxBytes = hexToBytes12(
12142
11833
  leaf.directFromCpfpRawUnsignedRefundTransaction
12143
11834
  );
12144
11835
  const directFromCpfpRefundTx = getTxFromRawTxBytes(
@@ -12154,7 +11845,7 @@ var SparkWallet = class extends EventEmitter {
12154
11845
  `Direct adaptor signed signature missing for node ${nodeId}`
12155
11846
  );
12156
11847
  }
12157
- const directFromCpfpAdaptorSignatureBytes = hexToBytes13(
11848
+ const directFromCpfpAdaptorSignatureBytes = hexToBytes12(
12158
11849
  leaf.directFromCpfpAdaptorSignedSignature
12159
11850
  );
12160
11851
  applyAdaptorToSignature(
@@ -12596,7 +12287,7 @@ var SparkWallet = class extends EventEmitter {
12596
12287
  }
12597
12288
  );
12598
12289
  }
12599
- const tx = new Transaction9();
12290
+ const tx = new Transaction7();
12600
12291
  tx.addInput({
12601
12292
  txid: depositTransactionId,
12602
12293
  index: outputIndex,
@@ -12636,7 +12327,7 @@ var SparkWallet = class extends EventEmitter {
12636
12327
  );
12637
12328
  const swapResponse = await sparkClient.initiate_static_deposit_utxo_refund({
12638
12329
  onChainUtxo: {
12639
- txid: hexToBytes13(depositTransactionId),
12330
+ txid: hexToBytes12(depositTransactionId),
12640
12331
  vout: outputIndex,
12641
12332
  network: networkType
12642
12333
  },
@@ -12763,7 +12454,7 @@ var SparkWallet = class extends EventEmitter {
12763
12454
  creditAmountView.setUint32(0, lowerHalf, true);
12764
12455
  creditAmountView.setUint32(4, upperHalf, true);
12765
12456
  parts.push(new Uint8Array(creditAmountBuffer));
12766
- parts.push(hexToBytes13(sspSignature));
12457
+ parts.push(hexToBytes12(sspSignature));
12767
12458
  const totalLength = parts.reduce((sum2, part) => sum2 + part.length, 0);
12768
12459
  const payload = new Uint8Array(totalLength);
12769
12460
  let offset = 0;
@@ -12867,26 +12558,7 @@ var SparkWallet = class extends EventEmitter {
12867
12558
  depositTx,
12868
12559
  vout
12869
12560
  });
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;
12561
+ return res.nodes;
12890
12562
  }
12891
12563
  /**
12892
12564
  * Gets all unused deposit addresses for the wallet.
@@ -13028,6 +12700,9 @@ var SparkWallet = class extends EventEmitter {
13028
12700
  depositTx,
13029
12701
  vout
13030
12702
  });
12703
+ await this.withLeaves(async () => {
12704
+ this.leaves.push(...nodes2);
12705
+ });
13031
12706
  return nodes2;
13032
12707
  });
13033
12708
  this.mutexes.delete(txid);
@@ -13157,7 +12832,7 @@ var SparkWallet = class extends EventEmitter {
13157
12832
  const [outcome] = await this.transferWithInvoice([
13158
12833
  {
13159
12834
  amountSats,
13160
- receiverIdentityPubkey: hexToBytes13(receiverAddress.identityPublicKey)
12835
+ receiverIdentityPubkey: hexToBytes12(receiverAddress.identityPublicKey)
13161
12836
  }
13162
12837
  ]);
13163
12838
  if (!outcome) throw new Error("no transfer created");
@@ -13204,8 +12879,7 @@ var SparkWallet = class extends EventEmitter {
13204
12879
  `TreeNode group at index ${groupIndex} not found for amount ${amount} after selection`
13205
12880
  );
13206
12881
  }
13207
- let available = await this.checkRefreshTimelockNodes(group);
13208
- available = await this.checkExtendTimeLockNodes(available);
12882
+ const available = await this.checkRenewLeaves(group);
13209
12883
  if (available.length < group.length) {
13210
12884
  throw new Error(
13211
12885
  `Not enough available nodes after refresh/extend. Expected ${group.length}, got ${available.length}`
@@ -13298,75 +12972,45 @@ var SparkWallet = class extends EventEmitter {
13298
12972
  newKeyDerivation: { type: "random" /* RANDOM */ }
13299
12973
  };
13300
12974
  }
13301
- async checkExtendTimeLockNodes(nodes) {
13302
- const nodesToExtend = [];
12975
+ async checkRenewLeaves(nodes) {
12976
+ const nodesToRenewNode = [];
12977
+ const nodesToRenewRefund = [];
12978
+ const nodesToRenewZeroTimelock = [];
13303
12979
  const nodeIds = [];
13304
12980
  const validNodes = [];
13305
12981
  for (const node of nodes) {
13306
12982
  const nodeTx = getTxFromRawTxBytes(node.nodeTx);
13307
- const sequence = nodeTx.getInput(0).sequence;
13308
- if (!sequence) {
12983
+ const refundTx = getTxFromRawTxBytes(node.refundTx);
12984
+ const nodeSequence = nodeTx.getInput(0).sequence;
12985
+ const refundSequence = refundTx.getInput(0).sequence;
12986
+ if (nodeSequence === void 0) {
13309
12987
  throw new ValidationError("Invalid node transaction", {
13310
12988
  field: "sequence",
13311
12989
  value: nodeTx.getInput(0),
13312
12990
  expected: "Non-null sequence"
13313
12991
  });
13314
12992
  }
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) {
12993
+ if (!refundSequence) {
13355
12994
  throw new ValidationError("Invalid refund transaction", {
13356
12995
  field: "sequence",
13357
12996
  value: refundTx.getInput(0),
13358
12997
  expected: "Non-null sequence"
13359
12998
  });
13360
12999
  }
13361
- const needsRefresh = doesLeafNeedRefresh(sequence);
13362
- if (needsRefresh) {
13363
- nodesToRefresh.push(node);
13000
+ if (doesTxnNeedRenewed(refundSequence)) {
13001
+ if (isZeroTimelock(nodeSequence)) {
13002
+ nodesToRenewZeroTimelock.push(node);
13003
+ } else if (doesTxnNeedRenewed(nodeSequence)) {
13004
+ nodesToRenewNode.push(node);
13005
+ } else {
13006
+ nodesToRenewRefund.push(node);
13007
+ }
13364
13008
  nodeIds.push(node.id);
13365
13009
  } else {
13366
13010
  validNodes.push(node);
13367
13011
  }
13368
13012
  }
13369
- if (nodesToRefresh.length === 0) {
13013
+ if (nodesToRenewNode.length === 0 && nodesToRenewRefund.length === 0 && nodesToRenewZeroTimelock.length === 0) {
13370
13014
  return validNodes;
13371
13015
  }
13372
13016
  const nodesResp = await this.queryNodes({
@@ -13384,7 +13028,18 @@ var SparkWallet = class extends EventEmitter {
13384
13028
  nodesMap.set(node.id, node);
13385
13029
  }
13386
13030
  const nodesToAdd = [];
13387
- for (const node of nodesToRefresh) {
13031
+ for (const node of nodesToRenewNode) {
13032
+ if (!node.parentNodeId) {
13033
+ throw new Error(`node ${node.id} has no parent`);
13034
+ }
13035
+ const parentNode = nodesMap.get(node.parentNodeId);
13036
+ if (!parentNode) {
13037
+ throw new Error(`parent node ${node.parentNodeId} not found`);
13038
+ }
13039
+ const newNode = await this.transferService.renewNodeTxn(node, parentNode);
13040
+ nodesToAdd.push(newNode);
13041
+ }
13042
+ for (const node of nodesToRenewRefund) {
13388
13043
  if (!node.parentNodeId) {
13389
13044
  throw new Error(`node ${node.id} has no parent`);
13390
13045
  }
@@ -13392,17 +13047,14 @@ var SparkWallet = class extends EventEmitter {
13392
13047
  if (!parentNode) {
13393
13048
  throw new Error(`parent node ${node.parentNodeId} not found`);
13394
13049
  }
13395
- const { nodes: nodes2 } = await this.transferService.refreshTimelockNodes(
13050
+ const newNode = await this.transferService.renewRefundTxn(
13396
13051
  node,
13397
13052
  parentNode
13398
13053
  );
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
- }
13054
+ nodesToAdd.push(newNode);
13055
+ }
13056
+ for (const node of nodesToRenewZeroTimelock) {
13057
+ const newNode = await this.transferService.renewZeroTimelockNodeTxn(node);
13406
13058
  nodesToAdd.push(newNode);
13407
13059
  }
13408
13060
  this.updateLeaves(nodeIds, nodesToAdd);
@@ -13444,8 +13096,7 @@ var SparkWallet = class extends EventEmitter {
13444
13096
  });
13445
13097
  }
13446
13098
  async processClaimedTransferResults(result, transfer, emit, optimize) {
13447
- result = await this.checkRefreshTimelockNodes(result);
13448
- result = await this.checkExtendTimeLockNodes(result);
13099
+ result = await this.checkRenewLeaves(result);
13449
13100
  const existingIds = new Set(this.leaves.map((leaf) => leaf.id));
13450
13101
  const uniqueResults = result.filter((node) => !existingIds.has(node.id));
13451
13102
  this.leaves.push(...uniqueResults);
@@ -13779,7 +13430,7 @@ var SparkWallet = class extends EventEmitter {
13779
13430
  const sparkFallbackAddress = decodedInvoice.fallbackAddress;
13780
13431
  const paymentHash = decodedInvoice.paymentHash;
13781
13432
  if (preferSpark) {
13782
- if (sparkFallbackAddress === void 0 || isValidSparkFallback(hexToBytes13(sparkFallbackAddress)) === false) {
13433
+ if (sparkFallbackAddress === void 0 || isValidSparkFallback(hexToBytes12(sparkFallbackAddress)) === false) {
13783
13434
  console.warn(
13784
13435
  "No valid spark address found in invoice. Defaulting to lightning."
13785
13436
  );
@@ -13824,8 +13475,7 @@ var SparkWallet = class extends EventEmitter {
13824
13475
  selectedLeaves,
13825
13476
  `no leaves for ${totalAmount}`
13826
13477
  );
13827
- leaves = await this.checkRefreshTimelockNodes(leaves);
13828
- leaves = await this.checkExtendTimeLockNodes(leaves);
13478
+ leaves = await this.checkRenewLeaves(leaves);
13829
13479
  const leavesToSend = await Promise.all(
13830
13480
  leaves.map(async (leaf) => ({
13831
13481
  leaf,
@@ -13841,17 +13491,17 @@ var SparkWallet = class extends EventEmitter {
13841
13491
  const transferID = uuidv74();
13842
13492
  const startTransferRequest = await this.transferService.prepareTransferForLightning(
13843
13493
  leavesToSend,
13844
- hexToBytes13(this.config.getSspIdentityPublicKey()),
13845
- hexToBytes13(paymentHash),
13494
+ hexToBytes12(this.config.getSspIdentityPublicKey()),
13495
+ hexToBytes12(paymentHash),
13846
13496
  expiryTime,
13847
13497
  transferID
13848
13498
  );
13849
13499
  const swapResponse = await this.lightningService.swapNodesForPreimage({
13850
13500
  leaves: leavesToSend,
13851
- receiverIdentityPubkey: hexToBytes13(
13501
+ receiverIdentityPubkey: hexToBytes12(
13852
13502
  this.config.getSspIdentityPublicKey()
13853
13503
  ),
13854
- paymentHash: hexToBytes13(paymentHash),
13504
+ paymentHash: hexToBytes12(paymentHash),
13855
13505
  isInboundPayment: false,
13856
13506
  invoiceString: invoice,
13857
13507
  feeSats: feeEstimate,
@@ -13954,7 +13604,7 @@ var SparkWallet = class extends EventEmitter {
13954
13604
  }
13955
13605
  satsInvoices.push({
13956
13606
  amountSats: encodedAmount ?? Number(amount),
13957
- receiverIdentityPubkey: hexToBytes13(addressData.identityPublicKey),
13607
+ receiverIdentityPubkey: hexToBytes12(addressData.identityPublicKey),
13958
13608
  sparkInvoice: invoice
13959
13609
  });
13960
13610
  } else if (fields.paymentType?.type === "tokens") {
@@ -14156,10 +13806,8 @@ var SparkWallet = class extends EventEmitter {
14156
13806
  leavesToSendToSsp = leavesForTargetAmount;
14157
13807
  leavesToSendToSE = leavesForFee;
14158
13808
  }
14159
- leavesToSendToSsp = await this.checkRefreshTimelockNodes(leavesToSendToSsp);
14160
- leavesToSendToSsp = await this.checkExtendTimeLockNodes(leavesToSendToSsp);
14161
- leavesToSendToSE = await this.checkRefreshTimelockNodes(leavesToSendToSE);
14162
- leavesToSendToSE = await this.checkExtendTimeLockNodes(leavesToSendToSE);
13809
+ leavesToSendToSsp = await this.checkRenewLeaves(leavesToSendToSsp);
13810
+ leavesToSendToSE = await this.checkRenewLeaves(leavesToSendToSE);
14163
13811
  const leafKeyTweaks = await Promise.all(
14164
13812
  [...leavesToSendToSE, ...leavesToSendToSsp].map(async (leaf) => ({
14165
13813
  leaf,
@@ -14204,11 +13852,11 @@ var SparkWallet = class extends EventEmitter {
14204
13852
  const connectorOutputs = [];
14205
13853
  for (let i = 0; i < connectorTx.outputsLength - 1; i++) {
14206
13854
  connectorOutputs.push({
14207
- txid: hexToBytes13(connectorTxId),
13855
+ txid: hexToBytes12(connectorTxId),
14208
13856
  index: i
14209
13857
  });
14210
13858
  }
14211
- const sspPubIdentityKey = hexToBytes13(this.config.getSspIdentityPublicKey());
13859
+ const sspPubIdentityKey = hexToBytes12(this.config.getSspIdentityPublicKey());
14212
13860
  const transfer = await this.coopExitService.getConnectorRefundSignatures({
14213
13861
  leaves: leafKeyTweaks,
14214
13862
  exitTxId: coopExitTxId,
@@ -14245,8 +13893,7 @@ var SparkWallet = class extends EventEmitter {
14245
13893
  (await this.selectLeaves([amountSats])).get(amountSats),
14246
13894
  `no leaves for ${amountSats}`
14247
13895
  );
14248
- leaves = await this.checkRefreshTimelockNodes(leaves);
14249
- leaves = await this.checkExtendTimeLockNodes(leaves);
13896
+ leaves = await this.checkRenewLeaves(leaves);
14250
13897
  const feeEstimate = await sspClient.getCoopExitFeeQuote({
14251
13898
  leafExternalIds: leaves.map((leaf) => leaf.id),
14252
13899
  withdrawalAddress
@@ -14521,7 +14168,7 @@ var SparkWallet = class extends EventEmitter {
14521
14168
  async validateMessageWithIdentityKey(message, signature) {
14522
14169
  const hash = sha25613(message);
14523
14170
  if (typeof signature === "string") {
14524
- signature = hexToBytes13(signature);
14171
+ signature = hexToBytes12(signature);
14525
14172
  }
14526
14173
  return this.config.signer.validateMessageWithIdentityKey(hash, signature);
14527
14174
  }
@@ -14534,7 +14181,7 @@ var SparkWallet = class extends EventEmitter {
14534
14181
  */
14535
14182
  async signTransaction(txHex, keyType = "auto-detect") {
14536
14183
  try {
14537
- const tx = Transaction9.fromRaw(hexToBytes13(txHex));
14184
+ const tx = Transaction7.fromRaw(hexToBytes12(txHex));
14538
14185
  let publicKey;
14539
14186
  switch (keyType.toLowerCase()) {
14540
14187
  case "identity":
@@ -14741,15 +14388,6 @@ var SparkWallet = class extends EventEmitter {
14741
14388
  }
14742
14389
  );
14743
14390
  }
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
14391
  const refundInput = refundTx.getInput(0);
14754
14392
  if (!refundInput) {
14755
14393
  throw new ValidationError(
@@ -14785,89 +14423,6 @@ var SparkWallet = class extends EventEmitter {
14785
14423
  );
14786
14424
  }
14787
14425
  }
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
14426
  cleanup() {
14872
14427
  if (this.claimTransfersInterval) {
14873
14428
  clearInterval(this.claimTransfersInterval);
@@ -15022,8 +14577,7 @@ var SparkWallet = class extends EventEmitter {
15022
14577
  "getLightningReceiveRequest",
15023
14578
  "getLightningSendRequest",
15024
14579
  "getCoopExitRequest",
15025
- "checkTimelock",
15026
- "testOnly_expireTimelock"
14580
+ "checkTimelock"
15027
14581
  ];
15028
14582
  methods.forEach((m) => this.wrapPublicSparkWalletMethodWithOtelSpan(m));
15029
14583
  this.initWallet = this.wrapWithOtelSpan(
@@ -15074,23 +14628,26 @@ export {
15074
14628
  HTLC_TIMELOCK_OFFSET,
15075
14629
  DIRECT_HTLC_TIMELOCK_OFFSET,
15076
14630
  INITIAL_SEQUENCE,
15077
- INITIAL_DIRECT_SEQUENCE,
15078
14631
  TEST_UNILATERAL_SEQUENCE,
15079
14632
  TEST_UNILATERAL_DIRECT_SEQUENCE,
15080
14633
  DEFAULT_FEE_SATS,
15081
14634
  maybeApplyFee,
15082
- createRootTx,
15083
- createSplitTx,
15084
- createNodeTx,
15085
- createNodeTxs,
15086
- createLeafNodeTx,
15087
- createRefundTx,
14635
+ createRootNodeTx,
14636
+ createZeroTimelockNodeTx,
14637
+ createInitialTimelockNodeTx,
14638
+ createDecrementedTimelockNodeTx,
14639
+ createTestUnilateralTimelockNodeTx,
15088
14640
  getNextHTLCTransactionSequence,
15089
- createRefundTxs,
15090
- createConnectorRefundTransactions,
14641
+ createInitialTimelockRefundTxs,
14642
+ createDecrementedTimelockRefundTxs,
14643
+ createCurrentTimelockRefundTxs,
14644
+ createTestUnilateralRefundTxs,
14645
+ createConnectorRefundTxs,
15091
14646
  getCurrentTimelock,
15092
14647
  getTransactionSequence,
15093
14648
  checkIfValidSequence,
14649
+ isZeroTimelock,
14650
+ doesTxnNeedRenewed,
15094
14651
  doesLeafNeedRefresh,
15095
14652
  getNextTransactionSequence,
15096
14653
  getEphemeralAnchorOutput,