@buildonspark/spark-sdk 0.3.8 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/android/build.gradle +1 -1
  3. package/android/src/main/java/uniffi/uniffi/spark_frost/spark_frost.kt +1361 -1367
  4. package/android/src/main/jniLibs/arm64-v8a/libuniffi_spark_frost.so +0 -0
  5. package/android/src/main/jniLibs/armeabi-v7a/libuniffi_spark_frost.so +0 -0
  6. package/android/src/main/jniLibs/x86/libuniffi_spark_frost.so +0 -0
  7. package/android/src/main/jniLibs/x86_64/libuniffi_spark_frost.so +0 -0
  8. package/dist/bare/index.cjs +1342 -1346
  9. package/dist/bare/index.d.cts +114 -68
  10. package/dist/bare/index.d.ts +114 -68
  11. package/dist/bare/index.js +1266 -1279
  12. package/dist/{chunk-FXIESWE6.js → chunk-27ILUWDJ.js} +1 -1
  13. package/dist/{chunk-5ASXVNTM.js → chunk-4YFT7DAE.js} +1 -1
  14. package/dist/{chunk-FP2CRVQH.js → chunk-G3LHXHF3.js} +1045 -1308
  15. package/dist/{chunk-XI6FCNYG.js → chunk-JLF6WJ7K.js} +1 -1
  16. package/dist/{chunk-3LPEQGVJ.js → chunk-LOXWCMZL.js} +1 -1
  17. package/dist/{chunk-5HU5W56H.js → chunk-WICAF6BS.js} +2 -2
  18. package/dist/{chunk-VFN34EOX.js → chunk-YEBEN7XD.js} +258 -0
  19. package/dist/{client-pNpGP15j.d.cts → client-BIqiUNy4.d.cts} +1 -1
  20. package/dist/{client-By-N7oJS.d.ts → client-BaQf-5gD.d.ts} +1 -1
  21. package/dist/debug.cjs +1330 -1336
  22. package/dist/debug.d.cts +20 -16
  23. package/dist/debug.d.ts +20 -16
  24. package/dist/debug.js +5 -5
  25. package/dist/graphql/objects/index.d.cts +3 -3
  26. package/dist/graphql/objects/index.d.ts +3 -3
  27. package/dist/index.cjs +1363 -1365
  28. package/dist/index.d.cts +7 -7
  29. package/dist/index.d.ts +7 -7
  30. package/dist/index.js +32 -24
  31. package/dist/index.node.cjs +1363 -1365
  32. package/dist/index.node.d.cts +7 -7
  33. package/dist/index.node.d.ts +7 -7
  34. package/dist/index.node.js +31 -23
  35. package/dist/{logging-DMFVY384.d.ts → logging-BNGm6dBp.d.ts} +42 -37
  36. package/dist/{logging-DxLp34Xm.d.cts → logging-D3IfXfHG.d.cts} +42 -37
  37. package/dist/native/index.react-native.cjs +1505 -1366
  38. package/dist/native/index.react-native.d.cts +112 -58
  39. package/dist/native/index.react-native.d.ts +112 -58
  40. package/dist/native/index.react-native.js +1415 -1289
  41. package/dist/proto/spark.cjs +258 -0
  42. package/dist/proto/spark.d.cts +1 -1
  43. package/dist/proto/spark.d.ts +1 -1
  44. package/dist/proto/spark.js +5 -1
  45. package/dist/proto/spark_token.d.cts +1 -1
  46. package/dist/proto/spark_token.d.ts +1 -1
  47. package/dist/proto/spark_token.js +2 -2
  48. package/dist/{spark-By6yHsrk.d.cts → spark-DOpheE8_.d.cts} +39 -2
  49. package/dist/{spark-By6yHsrk.d.ts → spark-DOpheE8_.d.ts} +39 -2
  50. package/dist/{spark-wallet.browser-C1dQknVj.d.ts → spark-wallet.browser-B2rGwjuM.d.ts} +2 -2
  51. package/dist/{spark-wallet.browser-CNMo3IvO.d.cts → spark-wallet.browser-Ck9No4Ks.d.cts} +2 -2
  52. package/dist/{spark-wallet.node-Og6__NMh.d.ts → spark-wallet.node-BqmKsGPs.d.ts} +2 -2
  53. package/dist/{spark-wallet.node-BZJhJZKq.d.cts → spark-wallet.node-C2TIkyt4.d.cts} +2 -2
  54. package/dist/tests/test-utils.cjs +1304 -1236
  55. package/dist/tests/test-utils.d.cts +18 -4
  56. package/dist/tests/test-utils.d.ts +18 -4
  57. package/dist/tests/test-utils.js +7 -7
  58. package/dist/{token-transactions-CLR3rnYi.d.cts → token-transactions-Db8mkjnU.d.cts} +2 -2
  59. package/dist/{token-transactions-C7yefB2S.d.ts → token-transactions-DoMcrxXQ.d.ts} +2 -2
  60. package/dist/types/index.cjs +256 -0
  61. package/dist/types/index.d.cts +2 -2
  62. package/dist/types/index.d.ts +2 -2
  63. package/dist/types/index.js +2 -2
  64. package/dist/{wallet-config-BoyMVa6n.d.ts → wallet-config-Bg3kWltL.d.ts} +42 -35
  65. package/dist/{wallet-config-xom-9UFF.d.cts → wallet-config-CuZKNo9S.d.cts} +42 -35
  66. package/ios/spark_frostFFI.xcframework/ios-arm64/SparkFrost +0 -0
  67. package/ios/spark_frostFFI.xcframework/ios-arm64/spark_frostFFI.framework/spark_frostFFI +0 -0
  68. package/ios/spark_frostFFI.xcframework/ios-arm64_x86_64-simulator/SparkFrost +0 -0
  69. package/ios/spark_frostFFI.xcframework/ios-arm64_x86_64-simulator/spark_frostFFI.framework/spark_frostFFI +0 -0
  70. package/ios/spark_frostFFI.xcframework/macos-arm64_x86_64/spark_frostFFI.framework/spark_frostFFI +0 -0
  71. package/package.json +1 -1
  72. package/src/index.react-native.ts +8 -2
  73. package/src/proto/spark.ts +348 -4
  74. package/src/services/config.ts +5 -0
  75. package/src/services/coop-exit.ts +26 -107
  76. package/src/services/deposit.ts +12 -48
  77. package/src/services/signing.ts +62 -49
  78. package/src/services/transfer.ts +437 -722
  79. package/src/services/wallet-config.ts +10 -0
  80. package/src/services/xhr-transport.ts +13 -3
  81. package/src/signer/signer.react-native.ts +73 -1
  82. package/src/spark-wallet/proto-descriptors.ts +1 -1
  83. package/src/spark-wallet/spark-wallet.ts +162 -315
  84. package/src/spark-wallet/types.ts +2 -2
  85. package/src/spark_descriptors.pb +0 -0
  86. package/src/tests/integration/lightning.test.ts +1 -27
  87. package/src/tests/integration/static_deposit.test.ts +6 -9
  88. package/src/tests/integration/unilateral-exit.test.ts +117 -0
  89. package/src/tests/optimize.test.ts +31 -1
  90. package/src/tests/utils/signing.ts +33 -0
  91. package/src/tests/utils/test-faucet.ts +61 -0
  92. package/src/utils/optimize.ts +42 -0
  93. package/src/utils/transaction.ts +250 -249
  94. package/src/utils/unilateral-exit.ts +1 -40
@@ -170,11 +170,11 @@ import {
170
170
  bytesToHex as bytesToHex11,
171
171
  bytesToNumberBE as bytesToNumberBE8,
172
172
  equalBytes as equalBytes6,
173
- hexToBytes as hexToBytes12
173
+ hexToBytes as hexToBytes11
174
174
  } from "@noble/curves/utils";
175
175
  import { validateMnemonic } from "@scure/bip39";
176
176
  import { wordlist as wordlist2 } from "@scure/bip39/wordlists/english";
177
- import { Address as Address4, OutScript as OutScript3, Transaction as Transaction8 } from "@scure/btc-signer";
177
+ import { Address as Address4, OutScript as OutScript3, Transaction as Transaction6 } from "@scure/btc-signer";
178
178
  import { Mutex } from "async-mutex";
179
179
  import { uuidv7 as uuidv74 } from "uuidv7";
180
180
 
@@ -4627,6 +4627,12 @@ var RenewLeafRequest = {
4627
4627
  writer.uint32(26).fork()
4628
4628
  ).join();
4629
4629
  break;
4630
+ case "renewNodeZeroTimelockSigningJob":
4631
+ RenewNodeZeroTimelockSigningJob.encode(
4632
+ message.signingJobs.renewNodeZeroTimelockSigningJob,
4633
+ writer.uint32(34).fork()
4634
+ ).join();
4635
+ break;
4630
4636
  }
4631
4637
  return writer;
4632
4638
  },
@@ -4664,6 +4670,16 @@ var RenewLeafRequest = {
4664
4670
  };
4665
4671
  continue;
4666
4672
  }
4673
+ case 4: {
4674
+ if (tag !== 34) {
4675
+ break;
4676
+ }
4677
+ message.signingJobs = {
4678
+ $case: "renewNodeZeroTimelockSigningJob",
4679
+ renewNodeZeroTimelockSigningJob: RenewNodeZeroTimelockSigningJob.decode(reader, reader.uint32())
4680
+ };
4681
+ continue;
4682
+ }
4667
4683
  }
4668
4684
  if ((tag & 7) === 4 || tag === 0) {
4669
4685
  break;
@@ -4681,6 +4697,11 @@ var RenewLeafRequest = {
4681
4697
  } : isSet3(object.renewRefundTimelockSigningJob) ? {
4682
4698
  $case: "renewRefundTimelockSigningJob",
4683
4699
  renewRefundTimelockSigningJob: RenewRefundTimelockSigningJob.fromJSON(object.renewRefundTimelockSigningJob)
4700
+ } : isSet3(object.renewNodeZeroTimelockSigningJob) ? {
4701
+ $case: "renewNodeZeroTimelockSigningJob",
4702
+ renewNodeZeroTimelockSigningJob: RenewNodeZeroTimelockSigningJob.fromJSON(
4703
+ object.renewNodeZeroTimelockSigningJob
4704
+ )
4684
4705
  } : void 0
4685
4706
  };
4686
4707
  },
@@ -4697,6 +4718,10 @@ var RenewLeafRequest = {
4697
4718
  obj.renewRefundTimelockSigningJob = RenewRefundTimelockSigningJob.toJSON(
4698
4719
  message.signingJobs.renewRefundTimelockSigningJob
4699
4720
  );
4721
+ } else if (message.signingJobs?.$case === "renewNodeZeroTimelockSigningJob") {
4722
+ obj.renewNodeZeroTimelockSigningJob = RenewNodeZeroTimelockSigningJob.toJSON(
4723
+ message.signingJobs.renewNodeZeroTimelockSigningJob
4724
+ );
4700
4725
  }
4701
4726
  return obj;
4702
4727
  },
@@ -4729,6 +4754,17 @@ var RenewLeafRequest = {
4729
4754
  }
4730
4755
  break;
4731
4756
  }
4757
+ case "renewNodeZeroTimelockSigningJob": {
4758
+ if (object.signingJobs?.renewNodeZeroTimelockSigningJob !== void 0 && object.signingJobs?.renewNodeZeroTimelockSigningJob !== null) {
4759
+ message.signingJobs = {
4760
+ $case: "renewNodeZeroTimelockSigningJob",
4761
+ renewNodeZeroTimelockSigningJob: RenewNodeZeroTimelockSigningJob.fromPartial(
4762
+ object.signingJobs.renewNodeZeroTimelockSigningJob
4763
+ )
4764
+ };
4765
+ }
4766
+ break;
4767
+ }
4732
4768
  }
4733
4769
  return message;
4734
4770
  }
@@ -5003,6 +5039,125 @@ var RenewRefundTimelockSigningJob = {
5003
5039
  return message;
5004
5040
  }
5005
5041
  };
5042
+ function createBaseRenewNodeZeroTimelockSigningJob() {
5043
+ return {
5044
+ nodeTxSigningJob: void 0,
5045
+ refundTxSigningJob: void 0,
5046
+ directNodeTxSigningJob: void 0,
5047
+ directRefundTxSigningJob: void 0,
5048
+ directFromCpfpRefundTxSigningJob: void 0
5049
+ };
5050
+ }
5051
+ var RenewNodeZeroTimelockSigningJob = {
5052
+ encode(message, writer = new BinaryWriter4()) {
5053
+ if (message.nodeTxSigningJob !== void 0) {
5054
+ UserSignedTxSigningJob.encode(message.nodeTxSigningJob, writer.uint32(10).fork()).join();
5055
+ }
5056
+ if (message.refundTxSigningJob !== void 0) {
5057
+ UserSignedTxSigningJob.encode(message.refundTxSigningJob, writer.uint32(18).fork()).join();
5058
+ }
5059
+ if (message.directNodeTxSigningJob !== void 0) {
5060
+ UserSignedTxSigningJob.encode(message.directNodeTxSigningJob, writer.uint32(26).fork()).join();
5061
+ }
5062
+ if (message.directRefundTxSigningJob !== void 0) {
5063
+ UserSignedTxSigningJob.encode(message.directRefundTxSigningJob, writer.uint32(34).fork()).join();
5064
+ }
5065
+ if (message.directFromCpfpRefundTxSigningJob !== void 0) {
5066
+ UserSignedTxSigningJob.encode(message.directFromCpfpRefundTxSigningJob, writer.uint32(42).fork()).join();
5067
+ }
5068
+ return writer;
5069
+ },
5070
+ decode(input, length) {
5071
+ const reader = input instanceof BinaryReader4 ? input : new BinaryReader4(input);
5072
+ const end = length === void 0 ? reader.len : reader.pos + length;
5073
+ const message = createBaseRenewNodeZeroTimelockSigningJob();
5074
+ while (reader.pos < end) {
5075
+ const tag = reader.uint32();
5076
+ switch (tag >>> 3) {
5077
+ case 1: {
5078
+ if (tag !== 10) {
5079
+ break;
5080
+ }
5081
+ message.nodeTxSigningJob = UserSignedTxSigningJob.decode(reader, reader.uint32());
5082
+ continue;
5083
+ }
5084
+ case 2: {
5085
+ if (tag !== 18) {
5086
+ break;
5087
+ }
5088
+ message.refundTxSigningJob = UserSignedTxSigningJob.decode(reader, reader.uint32());
5089
+ continue;
5090
+ }
5091
+ case 3: {
5092
+ if (tag !== 26) {
5093
+ break;
5094
+ }
5095
+ message.directNodeTxSigningJob = UserSignedTxSigningJob.decode(reader, reader.uint32());
5096
+ continue;
5097
+ }
5098
+ case 4: {
5099
+ if (tag !== 34) {
5100
+ break;
5101
+ }
5102
+ message.directRefundTxSigningJob = UserSignedTxSigningJob.decode(reader, reader.uint32());
5103
+ continue;
5104
+ }
5105
+ case 5: {
5106
+ if (tag !== 42) {
5107
+ break;
5108
+ }
5109
+ message.directFromCpfpRefundTxSigningJob = UserSignedTxSigningJob.decode(reader, reader.uint32());
5110
+ continue;
5111
+ }
5112
+ }
5113
+ if ((tag & 7) === 4 || tag === 0) {
5114
+ break;
5115
+ }
5116
+ reader.skip(tag & 7);
5117
+ }
5118
+ return message;
5119
+ },
5120
+ fromJSON(object) {
5121
+ return {
5122
+ nodeTxSigningJob: isSet3(object.nodeTxSigningJob) ? UserSignedTxSigningJob.fromJSON(object.nodeTxSigningJob) : void 0,
5123
+ refundTxSigningJob: isSet3(object.refundTxSigningJob) ? UserSignedTxSigningJob.fromJSON(object.refundTxSigningJob) : void 0,
5124
+ directNodeTxSigningJob: isSet3(object.directNodeTxSigningJob) ? UserSignedTxSigningJob.fromJSON(object.directNodeTxSigningJob) : void 0,
5125
+ directRefundTxSigningJob: isSet3(object.directRefundTxSigningJob) ? UserSignedTxSigningJob.fromJSON(object.directRefundTxSigningJob) : void 0,
5126
+ directFromCpfpRefundTxSigningJob: isSet3(object.directFromCpfpRefundTxSigningJob) ? UserSignedTxSigningJob.fromJSON(object.directFromCpfpRefundTxSigningJob) : void 0
5127
+ };
5128
+ },
5129
+ toJSON(message) {
5130
+ const obj = {};
5131
+ if (message.nodeTxSigningJob !== void 0) {
5132
+ obj.nodeTxSigningJob = UserSignedTxSigningJob.toJSON(message.nodeTxSigningJob);
5133
+ }
5134
+ if (message.refundTxSigningJob !== void 0) {
5135
+ obj.refundTxSigningJob = UserSignedTxSigningJob.toJSON(message.refundTxSigningJob);
5136
+ }
5137
+ if (message.directNodeTxSigningJob !== void 0) {
5138
+ obj.directNodeTxSigningJob = UserSignedTxSigningJob.toJSON(message.directNodeTxSigningJob);
5139
+ }
5140
+ if (message.directRefundTxSigningJob !== void 0) {
5141
+ obj.directRefundTxSigningJob = UserSignedTxSigningJob.toJSON(message.directRefundTxSigningJob);
5142
+ }
5143
+ if (message.directFromCpfpRefundTxSigningJob !== void 0) {
5144
+ obj.directFromCpfpRefundTxSigningJob = UserSignedTxSigningJob.toJSON(message.directFromCpfpRefundTxSigningJob);
5145
+ }
5146
+ return obj;
5147
+ },
5148
+ create(base) {
5149
+ return RenewNodeZeroTimelockSigningJob.fromPartial(base ?? {});
5150
+ },
5151
+ fromPartial(object) {
5152
+ const message = createBaseRenewNodeZeroTimelockSigningJob();
5153
+ message.nodeTxSigningJob = object.nodeTxSigningJob !== void 0 && object.nodeTxSigningJob !== null ? UserSignedTxSigningJob.fromPartial(object.nodeTxSigningJob) : void 0;
5154
+ message.refundTxSigningJob = object.refundTxSigningJob !== void 0 && object.refundTxSigningJob !== null ? UserSignedTxSigningJob.fromPartial(object.refundTxSigningJob) : void 0;
5155
+ message.directNodeTxSigningJob = object.directNodeTxSigningJob !== void 0 && object.directNodeTxSigningJob !== null ? UserSignedTxSigningJob.fromPartial(object.directNodeTxSigningJob) : void 0;
5156
+ message.directRefundTxSigningJob = object.directRefundTxSigningJob !== void 0 && object.directRefundTxSigningJob !== null ? UserSignedTxSigningJob.fromPartial(object.directRefundTxSigningJob) : void 0;
5157
+ message.directFromCpfpRefundTxSigningJob = object.directFromCpfpRefundTxSigningJob !== void 0 && object.directFromCpfpRefundTxSigningJob !== null ? UserSignedTxSigningJob.fromPartial(object.directFromCpfpRefundTxSigningJob) : void 0;
5158
+ return message;
5159
+ }
5160
+ };
5006
5161
  function createBaseRenewLeafResponse() {
5007
5162
  return { renewResult: void 0 };
5008
5163
  }
@@ -5015,6 +5170,9 @@ var RenewLeafResponse = {
5015
5170
  case "renewRefundTimelockResult":
5016
5171
  RenewRefundTimelockResult.encode(message.renewResult.renewRefundTimelockResult, writer.uint32(18).fork()).join();
5017
5172
  break;
5173
+ case "renewNodeZeroTimelockResult":
5174
+ RenewNodeZeroTimelockResult.encode(message.renewResult.renewNodeZeroTimelockResult, writer.uint32(26).fork()).join();
5175
+ break;
5018
5176
  }
5019
5177
  return writer;
5020
5178
  },
@@ -5045,6 +5203,16 @@ var RenewLeafResponse = {
5045
5203
  };
5046
5204
  continue;
5047
5205
  }
5206
+ case 3: {
5207
+ if (tag !== 26) {
5208
+ break;
5209
+ }
5210
+ message.renewResult = {
5211
+ $case: "renewNodeZeroTimelockResult",
5212
+ renewNodeZeroTimelockResult: RenewNodeZeroTimelockResult.decode(reader, reader.uint32())
5213
+ };
5214
+ continue;
5215
+ }
5048
5216
  }
5049
5217
  if ((tag & 7) === 4 || tag === 0) {
5050
5218
  break;
@@ -5061,6 +5229,9 @@ var RenewLeafResponse = {
5061
5229
  } : isSet3(object.renewRefundTimelockResult) ? {
5062
5230
  $case: "renewRefundTimelockResult",
5063
5231
  renewRefundTimelockResult: RenewRefundTimelockResult.fromJSON(object.renewRefundTimelockResult)
5232
+ } : isSet3(object.renewNodeZeroTimelockResult) ? {
5233
+ $case: "renewNodeZeroTimelockResult",
5234
+ renewNodeZeroTimelockResult: RenewNodeZeroTimelockResult.fromJSON(object.renewNodeZeroTimelockResult)
5064
5235
  } : void 0
5065
5236
  };
5066
5237
  },
@@ -5070,6 +5241,10 @@ var RenewLeafResponse = {
5070
5241
  obj.renewNodeTimelockResult = RenewNodeTimelockResult.toJSON(message.renewResult.renewNodeTimelockResult);
5071
5242
  } else if (message.renewResult?.$case === "renewRefundTimelockResult") {
5072
5243
  obj.renewRefundTimelockResult = RenewRefundTimelockResult.toJSON(message.renewResult.renewRefundTimelockResult);
5244
+ } else if (message.renewResult?.$case === "renewNodeZeroTimelockResult") {
5245
+ obj.renewNodeZeroTimelockResult = RenewNodeZeroTimelockResult.toJSON(
5246
+ message.renewResult.renewNodeZeroTimelockResult
5247
+ );
5073
5248
  }
5074
5249
  return obj;
5075
5250
  },
@@ -5099,6 +5274,17 @@ var RenewLeafResponse = {
5099
5274
  }
5100
5275
  break;
5101
5276
  }
5277
+ case "renewNodeZeroTimelockResult": {
5278
+ if (object.renewResult?.renewNodeZeroTimelockResult !== void 0 && object.renewResult?.renewNodeZeroTimelockResult !== null) {
5279
+ message.renewResult = {
5280
+ $case: "renewNodeZeroTimelockResult",
5281
+ renewNodeZeroTimelockResult: RenewNodeZeroTimelockResult.fromPartial(
5282
+ object.renewResult.renewNodeZeroTimelockResult
5283
+ )
5284
+ };
5285
+ }
5286
+ break;
5287
+ }
5102
5288
  }
5103
5289
  return message;
5104
5290
  }
@@ -5222,6 +5408,74 @@ var RenewRefundTimelockResult = {
5222
5408
  return message;
5223
5409
  }
5224
5410
  };
5411
+ function createBaseRenewNodeZeroTimelockResult() {
5412
+ return { splitNode: void 0, node: void 0 };
5413
+ }
5414
+ var RenewNodeZeroTimelockResult = {
5415
+ encode(message, writer = new BinaryWriter4()) {
5416
+ if (message.splitNode !== void 0) {
5417
+ TreeNode.encode(message.splitNode, writer.uint32(10).fork()).join();
5418
+ }
5419
+ if (message.node !== void 0) {
5420
+ TreeNode.encode(message.node, writer.uint32(18).fork()).join();
5421
+ }
5422
+ return writer;
5423
+ },
5424
+ decode(input, length) {
5425
+ const reader = input instanceof BinaryReader4 ? input : new BinaryReader4(input);
5426
+ const end = length === void 0 ? reader.len : reader.pos + length;
5427
+ const message = createBaseRenewNodeZeroTimelockResult();
5428
+ while (reader.pos < end) {
5429
+ const tag = reader.uint32();
5430
+ switch (tag >>> 3) {
5431
+ case 1: {
5432
+ if (tag !== 10) {
5433
+ break;
5434
+ }
5435
+ message.splitNode = TreeNode.decode(reader, reader.uint32());
5436
+ continue;
5437
+ }
5438
+ case 2: {
5439
+ if (tag !== 18) {
5440
+ break;
5441
+ }
5442
+ message.node = TreeNode.decode(reader, reader.uint32());
5443
+ continue;
5444
+ }
5445
+ }
5446
+ if ((tag & 7) === 4 || tag === 0) {
5447
+ break;
5448
+ }
5449
+ reader.skip(tag & 7);
5450
+ }
5451
+ return message;
5452
+ },
5453
+ fromJSON(object) {
5454
+ return {
5455
+ splitNode: isSet3(object.splitNode) ? TreeNode.fromJSON(object.splitNode) : void 0,
5456
+ node: isSet3(object.node) ? TreeNode.fromJSON(object.node) : void 0
5457
+ };
5458
+ },
5459
+ toJSON(message) {
5460
+ const obj = {};
5461
+ if (message.splitNode !== void 0) {
5462
+ obj.splitNode = TreeNode.toJSON(message.splitNode);
5463
+ }
5464
+ if (message.node !== void 0) {
5465
+ obj.node = TreeNode.toJSON(message.node);
5466
+ }
5467
+ return obj;
5468
+ },
5469
+ create(base) {
5470
+ return RenewNodeZeroTimelockResult.fromPartial(base ?? {});
5471
+ },
5472
+ fromPartial(object) {
5473
+ const message = createBaseRenewNodeZeroTimelockResult();
5474
+ message.splitNode = object.splitNode !== void 0 && object.splitNode !== null ? TreeNode.fromPartial(object.splitNode) : void 0;
5475
+ message.node = object.node !== void 0 && object.node !== null ? TreeNode.fromPartial(object.node) : void 0;
5476
+ return message;
5477
+ }
5478
+ };
5225
5479
  function createBaseNodeSignatureShares() {
5226
5480
  return {
5227
5481
  nodeId: "",
@@ -17976,7 +18230,11 @@ var BASE_CONFIG = {
17976
18230
  console: {
17977
18231
  otel: false
17978
18232
  },
17979
- events: {}
18233
+ events: {},
18234
+ optimizationOptions: {
18235
+ auto: true,
18236
+ multiplicity: 0
18237
+ }
17980
18238
  };
17981
18239
  var LOCAL_WALLET_CONFIG = {
17982
18240
  ...BASE_CONFIG,
@@ -18187,10 +18445,12 @@ var WalletConfigService = class {
18187
18445
  getEvents() {
18188
18446
  return this.config.events;
18189
18447
  }
18448
+ getOptimizationOptions() {
18449
+ return this.config.optimizationOptions;
18450
+ }
18190
18451
  };
18191
18452
 
18192
18453
  // src/services/coop-exit.ts
18193
- import { Transaction as Transaction4 } from "@scure/btc-signer";
18194
18454
  import { uuidv7 as uuidv72 } from "uuidv7";
18195
18455
 
18196
18456
  // src/utils/bitcoin.ts
@@ -18345,6 +18605,7 @@ function getTxEstimatedVbytesSizeByNumberOfInputsOutputs(numInputs, numOutputs)
18345
18605
  }
18346
18606
 
18347
18607
  // src/utils/transaction.ts
18608
+ import { hexToBytes as hexToBytes2 } from "@noble/hashes/utils";
18348
18609
  import { Transaction as Transaction2 } from "@scure/btc-signer";
18349
18610
  var INITIAL_TIMELOCK = 2e3;
18350
18611
  var TEST_UNILATERAL_TIMELOCK = 100;
@@ -18353,9 +18614,9 @@ var DIRECT_TIMELOCK_OFFSET = 50;
18353
18614
  var HTLC_TIMELOCK_OFFSET = 70;
18354
18615
  var DIRECT_HTLC_TIMELOCK_OFFSET = 85;
18355
18616
  var INITIAL_SEQUENCE = 1 << 30 | INITIAL_TIMELOCK;
18356
- var INITIAL_DIRECT_SEQUENCE = 1 << 30 | INITIAL_TIMELOCK + DIRECT_TIMELOCK_OFFSET;
18357
18617
  var TEST_UNILATERAL_SEQUENCE = 1 << 30 | TEST_UNILATERAL_TIMELOCK;
18358
18618
  var TEST_UNILATERAL_DIRECT_SEQUENCE = 1 << 30 | TEST_UNILATERAL_TIMELOCK + DIRECT_TIMELOCK_OFFSET;
18619
+ var INITIAL_ROOT_NODE_SEQUENCE = 1 << 30 | 0;
18359
18620
  var ESTIMATED_TX_SIZE = 191;
18360
18621
  var DEFAULT_SATS_PER_VBYTE = 5;
18361
18622
  var DEFAULT_FEE_SATS = ESTIMATED_TX_SIZE * DEFAULT_SATS_PER_VBYTE;
@@ -18365,63 +18626,8 @@ function maybeApplyFee(amount) {
18365
18626
  }
18366
18627
  return amount;
18367
18628
  }
18368
- function createRootTx(depositOutPoint, depositTxOut) {
18369
- const cpfpRootTx = new Transaction2({
18370
- version: 3,
18371
- allowUnknownOutputs: true
18372
- });
18373
- cpfpRootTx.addInput(depositOutPoint);
18374
- cpfpRootTx.addOutput(depositTxOut);
18375
- cpfpRootTx.addOutput(getEphemeralAnchorOutput());
18376
- const directRootTx = new Transaction2({
18377
- version: 3,
18378
- allowUnknownOutputs: true
18379
- });
18380
- directRootTx.addInput(depositOutPoint);
18381
- directRootTx.addOutput({
18382
- script: depositTxOut.script,
18383
- amount: maybeApplyFee(depositTxOut.amount ?? 0n)
18384
- });
18385
- return [cpfpRootTx, directRootTx];
18386
- }
18387
- function createSplitTx(parentOutPoint, childTxOuts) {
18388
- const cpfpSplitTx = new Transaction2({
18389
- version: 3,
18390
- allowUnknownOutputs: true
18391
- });
18392
- cpfpSplitTx.addInput(parentOutPoint);
18393
- for (const txOut of childTxOuts) {
18394
- cpfpSplitTx.addOutput(txOut);
18395
- }
18396
- cpfpSplitTx.addOutput(getEphemeralAnchorOutput());
18397
- const directSplitTx = new Transaction2({
18398
- version: 3,
18399
- allowUnknownOutputs: true
18400
- });
18401
- directSplitTx.addInput(parentOutPoint);
18402
- let totalOutputAmount = 0n;
18403
- for (const txOut of childTxOuts) {
18404
- totalOutputAmount += txOut.amount ?? 0n;
18405
- }
18406
- if (totalOutputAmount > BigInt(DEFAULT_FEE_SATS)) {
18407
- const feeRatio = Number(DEFAULT_FEE_SATS) / Number(totalOutputAmount);
18408
- for (const txOut of childTxOuts) {
18409
- const adjustedAmount = BigInt(
18410
- Math.floor(Number(txOut.amount ?? 0n) * (1 - feeRatio))
18411
- );
18412
- directSplitTx.addOutput({
18413
- script: txOut.script,
18414
- amount: adjustedAmount
18415
- });
18416
- }
18417
- } else {
18418
- for (const txOut of childTxOuts) {
18419
- directSplitTx.addOutput(txOut);
18420
- }
18421
- }
18422
- return [cpfpSplitTx, directSplitTx];
18423
- }
18424
18629
  function createNodeTx({
18630
+ sequence,
18425
18631
  txOut,
18426
18632
  parentOutPoint,
18427
18633
  applyFee,
@@ -18431,7 +18637,10 @@ function createNodeTx({
18431
18637
  version: 3,
18432
18638
  allowUnknownOutputs: true
18433
18639
  });
18434
- nodeTx.addInput(parentOutPoint);
18640
+ nodeTx.addInput({
18641
+ ...parentOutPoint,
18642
+ sequence
18643
+ });
18435
18644
  if (applyFee) {
18436
18645
  nodeTx.addOutput({
18437
18646
  script: txOut.script,
@@ -18445,52 +18654,92 @@ function createNodeTx({
18445
18654
  }
18446
18655
  return nodeTx;
18447
18656
  }
18448
- function createNodeTxs(txOut, txIn, directTxIn) {
18449
- const cpfpNodeTx = createNodeTx({
18450
- txOut,
18451
- parentOutPoint: txIn,
18452
- includeAnchor: true
18453
- });
18454
- let directNodeTx;
18455
- if (directTxIn) {
18456
- directNodeTx = createNodeTx({
18457
- txOut,
18458
- parentOutPoint: directTxIn,
18459
- includeAnchor: false,
18460
- applyFee: true
18657
+ function createNodeTxs({
18658
+ parentTx,
18659
+ sequence,
18660
+ directSequence,
18661
+ vout
18662
+ }) {
18663
+ const parentOutput = parentTx.getOutput(vout);
18664
+ if (!parentOutput.amount || !parentOutput.script) {
18665
+ throw new ValidationError("Parent output amount or script not found", {
18666
+ field: "parentOutput",
18667
+ value: parentOutput
18461
18668
  });
18462
18669
  }
18463
- return { cpfpNodeTx, directNodeTx };
18670
+ const output = {
18671
+ script: parentOutput.script,
18672
+ amount: parentOutput.amount
18673
+ };
18674
+ const input = {
18675
+ txid: hexToBytes2(getTxId(parentTx)),
18676
+ index: vout
18677
+ };
18678
+ const nodeTx = createNodeTx({
18679
+ sequence,
18680
+ txOut: output,
18681
+ parentOutPoint: input,
18682
+ includeAnchor: true
18683
+ });
18684
+ const directNodeTx = createNodeTx({
18685
+ sequence: directSequence ?? sequence + DIRECT_TIMELOCK_OFFSET,
18686
+ txOut: output,
18687
+ parentOutPoint: input,
18688
+ includeAnchor: false,
18689
+ applyFee: true
18690
+ });
18691
+ return { nodeTx, directNodeTx };
18464
18692
  }
18465
- function createLeafNodeTx(sequence, directSequence, parentOutPoint, txOut, shouldCalculateFee) {
18466
- const cpfpLeafTx = new Transaction2({
18467
- version: 3,
18468
- allowUnknownOutputs: true
18693
+ function createRootNodeTx(parentTx, vout) {
18694
+ return createNodeTxs({
18695
+ parentTx,
18696
+ sequence: INITIAL_ROOT_NODE_SEQUENCE,
18697
+ vout
18469
18698
  });
18470
- cpfpLeafTx.addInput({
18471
- ...parentOutPoint,
18472
- sequence
18699
+ }
18700
+ function createZeroTimelockNodeTx(parentTx) {
18701
+ return createNodeTxs({
18702
+ parentTx,
18703
+ sequence: INITIAL_ROOT_NODE_SEQUENCE,
18704
+ directSequence: DIRECT_TIMELOCK_OFFSET,
18705
+ vout: 0
18473
18706
  });
18474
- cpfpLeafTx.addOutput(txOut);
18475
- cpfpLeafTx.addOutput(getEphemeralAnchorOutput());
18476
- const directLeafTx = new Transaction2({
18477
- version: 3,
18478
- allowUnknownOutputs: true
18707
+ }
18708
+ function createInitialTimelockNodeTx(parentTx) {
18709
+ return createNodeTxs({
18710
+ parentTx,
18711
+ sequence: INITIAL_SEQUENCE,
18712
+ vout: 0
18479
18713
  });
18480
- directLeafTx.addInput({
18481
- ...parentOutPoint,
18482
- sequence: directSequence
18714
+ }
18715
+ function createDecrementedTimelockNodeTx(parentTx, currentTx) {
18716
+ const currentSequence = currentTx.getInput(0).sequence;
18717
+ if (!currentSequence) {
18718
+ throw new ValidationError("Current sequence not found", {
18719
+ field: "currentSequence",
18720
+ value: currentSequence
18721
+ });
18722
+ }
18723
+ return createNodeTxs({
18724
+ parentTx,
18725
+ sequence: getNextTransactionSequence(currentSequence).nextSequence,
18726
+ vout: 0
18483
18727
  });
18484
- const amountSats = txOut.amount ?? 0n;
18485
- let outputAmount = amountSats;
18486
- if (shouldCalculateFee) {
18487
- outputAmount = maybeApplyFee(amountSats);
18728
+ }
18729
+ function createTestUnilateralTimelockNodeTx(parentTx, nodeTx) {
18730
+ const sequence = nodeTx.getInput(0).sequence;
18731
+ if (!sequence) {
18732
+ throw new ValidationError("Sequence not found", {
18733
+ field: "sequence",
18734
+ value: sequence
18735
+ });
18488
18736
  }
18489
- directLeafTx.addOutput({
18490
- script: txOut.script,
18491
- amount: outputAmount
18737
+ const isBit30Defined = (sequence || 0) & 1 << 30;
18738
+ return createNodeTxs({
18739
+ parentTx,
18740
+ sequence: isBit30Defined | TEST_UNILATERAL_TIMELOCK,
18741
+ vout: 0
18492
18742
  });
18493
- return [cpfpLeafTx, directLeafTx];
18494
18743
  }
18495
18744
  function createRefundTx({
18496
18745
  sequence,
@@ -18546,110 +18795,109 @@ function getNextHTLCTransactionSequence(currSequence, isNodeTx) {
18546
18795
  };
18547
18796
  }
18548
18797
  function createRefundTxs({
18549
- sequence,
18550
- directSequence,
18551
- input,
18552
- directInput,
18553
- amountSats,
18798
+ nodeTx,
18799
+ directNodeTx,
18554
18800
  receivingPubkey,
18555
- network
18801
+ network,
18802
+ sequence
18556
18803
  }) {
18557
- const cpfpRefundTx = createRefundTx({
18558
- sequence,
18559
- input,
18560
- amountSats,
18561
- receivingPubkey,
18562
- network,
18563
- shouldCalculateFee: false,
18564
- includeAnchor: true
18565
- });
18804
+ const refundInput = {
18805
+ txid: hexToBytes2(getTxId(nodeTx)),
18806
+ index: 0
18807
+ };
18808
+ const nodeAmountSats = nodeTx.getOutput(0).amount;
18809
+ if (nodeAmountSats === void 0) {
18810
+ throw new ValidationError("Node amount not found", {
18811
+ field: "nodeAmountSats",
18812
+ value: nodeAmountSats
18813
+ });
18814
+ }
18566
18815
  let directRefundTx;
18567
- let directFromCpfpRefundTx;
18568
- if (directSequence && directInput) {
18816
+ if (directNodeTx) {
18817
+ const directRefundInput = {
18818
+ txid: hexToBytes2(getTxId(directNodeTx)),
18819
+ index: 0
18820
+ };
18821
+ const directAmountSats = directNodeTx.getOutput(0).amount;
18822
+ if (directAmountSats === void 0) {
18823
+ throw new ValidationError("Direct amount not found", {
18824
+ field: "directAmountSats",
18825
+ value: directAmountSats
18826
+ });
18827
+ }
18569
18828
  directRefundTx = createRefundTx({
18570
- sequence: directSequence,
18571
- input: directInput,
18572
- amountSats,
18573
- receivingPubkey,
18574
- network,
18575
- shouldCalculateFee: true,
18576
- includeAnchor: false
18577
- });
18578
- directFromCpfpRefundTx = createRefundTx({
18579
- sequence: directSequence,
18580
- input,
18581
- amountSats,
18829
+ sequence: sequence + DIRECT_TIMELOCK_OFFSET,
18830
+ input: directRefundInput,
18831
+ amountSats: directAmountSats,
18582
18832
  receivingPubkey,
18583
18833
  network,
18584
18834
  shouldCalculateFee: true,
18585
18835
  includeAnchor: false
18586
18836
  });
18587
- } else if (directInput && !directSequence) {
18588
- throw new ValidationError(
18589
- "directSequence must be provided if directInput is",
18590
- {
18591
- field: "directSequence",
18592
- value: directSequence
18593
- }
18594
- );
18595
18837
  }
18596
- return { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx };
18597
- }
18598
- function createConnectorRefundTransactions(sequence, cpfpNodeOutPoint, directNodeOutPoint, connectorOutput, amountSats, receiverPubKey, network, shouldCalculateFee) {
18599
- const cpfpRefundTx = new Transaction2({
18600
- version: 3,
18601
- allowUnknownOutputs: true
18838
+ const cpfpRefundTx = createRefundTx({
18839
+ sequence,
18840
+ input: refundInput,
18841
+ amountSats: nodeAmountSats,
18842
+ receivingPubkey,
18843
+ network,
18844
+ shouldCalculateFee: false,
18845
+ includeAnchor: true
18602
18846
  });
18603
- cpfpRefundTx.addInput({
18604
- ...cpfpNodeOutPoint,
18605
- sequence
18847
+ const directFromCpfpRefundTx = createRefundTx({
18848
+ sequence: sequence + DIRECT_TIMELOCK_OFFSET,
18849
+ input: refundInput,
18850
+ amountSats: nodeAmountSats,
18851
+ receivingPubkey,
18852
+ network,
18853
+ shouldCalculateFee: true,
18854
+ includeAnchor: false
18606
18855
  });
18607
- cpfpRefundTx.addInput(connectorOutput);
18608
- const receiverScript = getP2TRScriptFromPublicKey(receiverPubKey, network);
18609
- cpfpRefundTx.addOutput({
18610
- script: receiverScript,
18611
- amount: amountSats
18856
+ return { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx };
18857
+ }
18858
+ function createInitialTimelockRefundTxs(params) {
18859
+ return createRefundTxs({
18860
+ ...params,
18861
+ sequence: INITIAL_SEQUENCE
18612
18862
  });
18613
- const directRefundTx = new Transaction2({
18614
- version: 3,
18615
- allowUnknownOutputs: true
18863
+ }
18864
+ function createDecrementedTimelockRefundTxs(params) {
18865
+ const nextSequence = getNextTransactionSequence(params.sequence).nextSequence;
18866
+ return createRefundTxs({
18867
+ ...params,
18868
+ sequence: nextSequence
18616
18869
  });
18617
- directRefundTx.addInput({
18618
- ...directNodeOutPoint,
18619
- sequence
18870
+ }
18871
+ function createCurrentTimelockRefundTxs(params) {
18872
+ return createRefundTxs(params);
18873
+ }
18874
+ function createTestUnilateralRefundTxs(params) {
18875
+ return createRefundTxs({
18876
+ ...params,
18877
+ sequence: TEST_UNILATERAL_SEQUENCE
18620
18878
  });
18621
- directRefundTx.addInput(connectorOutput);
18622
- let outputAmount = amountSats;
18623
- if (shouldCalculateFee) {
18624
- outputAmount = maybeApplyFee(amountSats);
18879
+ }
18880
+ function createConnectorRefundTxs(params) {
18881
+ const { connectorOutput, ...baseParams } = params;
18882
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createDecrementedTimelockRefundTxs(baseParams);
18883
+ cpfpRefundTx.addInput(connectorOutput);
18884
+ if (directRefundTx) {
18885
+ directRefundTx.addInput(connectorOutput);
18625
18886
  }
18626
- directRefundTx.addOutput({
18627
- script: receiverScript,
18628
- amount: outputAmount
18629
- });
18630
- const directFromCpfpTx = new Transaction2({
18631
- version: 3,
18632
- allowUnknownOutputs: true
18633
- });
18634
- directFromCpfpTx.addInput({
18635
- ...cpfpNodeOutPoint,
18636
- sequence
18637
- });
18638
- directFromCpfpTx.addInput(connectorOutput);
18639
- directFromCpfpTx.addOutput({
18640
- script: receiverScript,
18641
- amount: outputAmount
18642
- });
18643
- return [cpfpRefundTx, directRefundTx, directFromCpfpTx];
18887
+ if (directFromCpfpRefundTx) {
18888
+ directFromCpfpRefundTx.addInput(connectorOutput);
18889
+ }
18890
+ return { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx };
18644
18891
  }
18645
18892
  function getCurrentTimelock(currSequence) {
18646
18893
  return (currSequence || 0) & 65535;
18647
18894
  }
18648
18895
  function getTransactionSequence(currSequence) {
18649
18896
  const timelock = getCurrentTimelock(currSequence);
18897
+ const isBit30Defined = (currSequence || 0) & 1 << 30;
18650
18898
  return {
18651
- nextSequence: 1 << 30 | timelock,
18652
- nextDirectSequence: 1 << 30 | timelock + DIRECT_TIMELOCK_OFFSET
18899
+ nextSequence: isBit30Defined | timelock,
18900
+ nextDirectSequence: isBit30Defined | timelock + DIRECT_TIMELOCK_OFFSET
18653
18901
  };
18654
18902
  }
18655
18903
  function checkIfValidSequence(currSequence) {
@@ -18668,6 +18916,13 @@ function checkIfValidSequence(currSequence) {
18668
18916
  });
18669
18917
  }
18670
18918
  }
18919
+ function isZeroTimelock(currSequence) {
18920
+ return getCurrentTimelock(currSequence) === 0;
18921
+ }
18922
+ function doesTxnNeedRenewed(currSequence) {
18923
+ const currentTimelock = getCurrentTimelock(currSequence);
18924
+ return currentTimelock <= 100;
18925
+ }
18671
18926
  function doesLeafNeedRefresh(currSequence, isNodeTx) {
18672
18927
  const currentTimelock = getCurrentTimelock(currSequence);
18673
18928
  if (isNodeTx) {
@@ -18678,6 +18933,7 @@ function doesLeafNeedRefresh(currSequence, isNodeTx) {
18678
18933
  function getNextTransactionSequence(currSequence, isNodeTx) {
18679
18934
  const currentTimelock = getCurrentTimelock(currSequence);
18680
18935
  const nextTimelock = currentTimelock - TIME_LOCK_INTERVAL;
18936
+ const isBit30Defined = (currSequence || 0) & 1 << 30;
18681
18937
  if (isNodeTx && nextTimelock < 0) {
18682
18938
  throw new ValidationError("timelock interval is less than 0", {
18683
18939
  field: "nextTimelock",
@@ -18692,8 +18948,8 @@ function getNextTransactionSequence(currSequence, isNodeTx) {
18692
18948
  });
18693
18949
  }
18694
18950
  return {
18695
- nextSequence: 1 << 30 | nextTimelock,
18696
- nextDirectSequence: 1 << 30 | nextTimelock + DIRECT_TIMELOCK_OFFSET
18951
+ nextSequence: isBit30Defined | nextTimelock,
18952
+ nextDirectSequence: isBit30Defined | nextTimelock + DIRECT_TIMELOCK_OFFSET
18697
18953
  };
18698
18954
  }
18699
18955
  function getEphemeralAnchorOutput() {
@@ -18706,19 +18962,13 @@ function getEphemeralAnchorOutput() {
18706
18962
 
18707
18963
  // src/services/transfer.ts
18708
18964
  import { secp256k1 as secp256k12 } from "@noble/curves/secp256k1";
18709
- import {
18710
- bytesToHex as bytesToHex4,
18711
- equalBytes,
18712
- hexToBytes as hexToBytes3,
18713
- numberToBytesBE
18714
- } from "@noble/curves/utils";
18965
+ import { bytesToHex as bytesToHex4, equalBytes, numberToBytesBE } from "@noble/curves/utils";
18715
18966
  import { sha256 as sha2564 } from "@noble/hashes/sha2";
18716
- import { Transaction as Transaction3 } from "@scure/btc-signer";
18717
18967
  import * as ecies from "eciesjs";
18718
18968
  import { uuidv7 } from "uuidv7";
18719
18969
 
18720
18970
  // src/utils/transfer_package.ts
18721
- import { hexToBytes as hexToBytes2 } from "@noble/curves/utils";
18971
+ import { hexToBytes as hexToBytes3 } from "@noble/curves/utils";
18722
18972
  import { sha256 as sha2563 } from "@noble/hashes/sha2";
18723
18973
  function getTransferPackageSigningPayload(transferID, transferPackage) {
18724
18974
  const encryptedPayload = transferPackage.keyTweakPackage;
@@ -18727,7 +18977,7 @@ function getTransferPackageSigningPayload(transferID, transferPackage) {
18727
18977
  ).map(([key, value]) => ({ key, value }));
18728
18978
  pairs.sort((a, b) => a.key.localeCompare(b.key));
18729
18979
  const encoder = new TextEncoder();
18730
- let message = hexToBytes2(transferID.replaceAll("-", ""));
18980
+ let message = hexToBytes3(transferID.replaceAll("-", ""));
18731
18981
  for (const pair of pairs) {
18732
18982
  const keyPart = encoder.encode(pair.key + ":");
18733
18983
  const separator = encoder.encode(";");
@@ -18742,13 +18992,6 @@ function getTransferPackageSigningPayload(transferID, transferPackage) {
18742
18992
  }
18743
18993
 
18744
18994
  // src/services/transfer.ts
18745
- function getSigningJobProto(signingJob) {
18746
- return {
18747
- signingPublicKey: signingJob.signingPublicKey,
18748
- rawTx: signingJob.rawTx,
18749
- signingNonceCommitment: signingJob.signingNonceCommitment.commitment
18750
- };
18751
- }
18752
18995
  var BaseTransferService = class {
18753
18996
  config;
18754
18997
  connectionManager;
@@ -19430,42 +19673,27 @@ var TransferService = class extends BaseTransferService {
19430
19673
  throw new Error(`Leaf data not found for leaf ${leaf.leaf.id}`);
19431
19674
  }
19432
19675
  const nodeTx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
19433
- const cpfpNodeOutPoint = {
19434
- txid: hexToBytes3(getTxId(nodeTx)),
19435
- index: 0
19436
- };
19437
19676
  let directNodeTx;
19438
- let directNodeOutPoint;
19439
19677
  if (leaf.leaf.directTx.length > 0) {
19440
19678
  directNodeTx = getTxFromRawTxBytes(leaf.leaf.directTx);
19441
- directNodeOutPoint = {
19442
- txid: hexToBytes3(getTxId(directNodeTx)),
19443
- index: 0
19444
- };
19445
19679
  }
19446
19680
  const currRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
19447
- const sequence = currRefundTx.getInput(0).sequence;
19448
- if (!sequence) {
19681
+ const currentSequence = currRefundTx.getInput(0).sequence;
19682
+ if (!currentSequence) {
19449
19683
  throw new ValidationError("Invalid refund transaction", {
19450
19684
  field: "sequence",
19451
19685
  value: currRefundTx.getInput(0),
19452
19686
  expected: "Non-null sequence"
19453
19687
  });
19454
19688
  }
19455
- const { nextSequence, nextDirectSequence } = isForClaim ? getTransactionSequence(sequence) : getNextTransactionSequence(sequence);
19456
- const amountSats = currRefundTx.getOutput(0).amount;
19457
- if (amountSats === void 0) {
19458
- throw new Error("Amount not found in signRefunds");
19459
- }
19460
- const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
19461
- sequence: nextSequence,
19462
- directSequence: nextDirectSequence,
19463
- input: cpfpNodeOutPoint,
19464
- directInput: directNodeOutPoint,
19465
- amountSats,
19689
+ const refundTxsParams = {
19690
+ nodeTx,
19691
+ directNodeTx,
19692
+ sequence: currentSequence,
19466
19693
  receivingPubkey: refundSigningData.receivingPubkey,
19467
19694
  network: this.config.getNetwork()
19468
- });
19695
+ };
19696
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = isForClaim ? createCurrentTimelockRefundTxs(refundTxsParams) : createDecrementedTimelockRefundTxs(refundTxsParams);
19469
19697
  refundSigningData.refundTx = cpfpRefundTx;
19470
19698
  refundSigningData.directRefundTx = directRefundTx;
19471
19699
  refundSigningData.directFromCpfpRefundTx = directFromCpfpRefundTx;
@@ -19678,71 +19906,127 @@ var TransferService = class extends BaseTransferService {
19678
19906
  throw new Error(`Error querying pending transfers by sender: ${error}`);
19679
19907
  }
19680
19908
  }
19681
- async refreshTimelockNodesInternal(node, parentNode, useTestUnilateralSequence) {
19682
- const signingJobs = [];
19683
- const parentNodeTx = getTxFromRawTxBytes(parentNode.nodeTx);
19684
- const parentNodeOutput = parentNodeTx.getOutput(0);
19685
- if (!parentNodeOutput) {
19686
- throw Error("Could not get parent node output");
19909
+ async renewRefundTxn(node, parentNode) {
19910
+ const sparkClient = await this.connectionManager.createSparkClient(
19911
+ this.config.getCoordinatorAddress()
19912
+ );
19913
+ const signingJobs = await this.createRenewRefundSigningJobs(
19914
+ node,
19915
+ parentNode
19916
+ );
19917
+ const statechainCommitments = await sparkClient.get_signing_commitments({
19918
+ nodeIds: [node.id],
19919
+ count: signingJobs.length
19920
+ });
19921
+ const mappedSigningJobs = signingJobs.map((signingJob, index) => {
19922
+ const signingNonceCommitments = statechainCommitments.signingCommitments[index]?.signingNonceCommitments;
19923
+ if (!signingNonceCommitments) {
19924
+ throw new Error("Signing nonce commitments not found");
19925
+ }
19926
+ return {
19927
+ ...signingJob,
19928
+ signingNonceCommitments
19929
+ };
19930
+ });
19931
+ const userSignedTxSigningJobs = await this.signingService.signSigningJobs(mappedSigningJobs);
19932
+ const renewRefundTimelockSigningJob = {
19933
+ nodeTxSigningJob: userSignedTxSigningJobs.get("node"),
19934
+ refundTxSigningJob: userSignedTxSigningJobs.get("cpfp"),
19935
+ directNodeTxSigningJob: userSignedTxSigningJobs.get("directNode"),
19936
+ directRefundTxSigningJob: userSignedTxSigningJobs.get("direct"),
19937
+ directFromCpfpRefundTxSigningJob: userSignedTxSigningJobs.get("directFromCpfp")
19938
+ };
19939
+ const response = await sparkClient.renew_leaf({
19940
+ leafId: node.id,
19941
+ signingJobs: {
19942
+ $case: "renewRefundTimelockSigningJob",
19943
+ renewRefundTimelockSigningJob
19944
+ }
19945
+ });
19946
+ if (response.renewResult?.$case !== "renewRefundTimelockResult" || !response.renewResult?.renewRefundTimelockResult.node) {
19947
+ throw new ValidationError("Unexpected renew result", {
19948
+ field: "renewResult",
19949
+ value: response.renewResult
19950
+ });
19687
19951
  }
19688
- const nodeTx = getTxFromRawTxBytes(node.nodeTx);
19689
- const nodeInput = nodeTx.getInput(0);
19690
- const nodeOutput = nodeTx.getOutput(0);
19691
- if (!nodeOutput) {
19692
- throw Error("Could not get node output");
19693
- }
19694
- let directNodeTx;
19695
- let directNodeInput;
19696
- if (node.directTx.length > 0) {
19697
- directNodeTx = getTxFromRawTxBytes(node.directTx);
19698
- directNodeInput = directNodeTx.getInput(0);
19699
- }
19700
- const currSequence = nodeInput.sequence;
19701
- if (!currSequence) {
19702
- throw new ValidationError("Invalid node transaction", {
19703
- field: "sequence",
19704
- value: nodeInput,
19705
- expected: "Non-null sequence"
19952
+ return response.renewResult?.renewRefundTimelockResult.node;
19953
+ }
19954
+ async renewNodeTxn(node, parentNode) {
19955
+ const sparkClient = await this.connectionManager.createSparkClient(
19956
+ this.config.getCoordinatorAddress()
19957
+ );
19958
+ const signingJobs = await this.createRenewNodeSigningJobs(node, parentNode);
19959
+ const statechainCommitments = await sparkClient.get_signing_commitments({
19960
+ nodeIds: [node.id],
19961
+ count: signingJobs.length
19962
+ });
19963
+ const mappedSigningJobs = signingJobs.map((signingJob, index) => {
19964
+ const signingNonceCommitments = statechainCommitments.signingCommitments[index]?.signingNonceCommitments;
19965
+ if (!signingNonceCommitments) {
19966
+ throw new Error("Signing nonce commitments not found");
19967
+ }
19968
+ return {
19969
+ ...signingJob,
19970
+ signingNonceCommitments
19971
+ };
19972
+ });
19973
+ const userSignedTxSigningJobs = await this.signingService.signSigningJobs(mappedSigningJobs);
19974
+ const response = await sparkClient.renew_leaf({
19975
+ leafId: node.id,
19976
+ signingJobs: {
19977
+ $case: "renewNodeTimelockSigningJob",
19978
+ renewNodeTimelockSigningJob: {
19979
+ splitNodeTxSigningJob: userSignedTxSigningJobs.get("split"),
19980
+ splitNodeDirectTxSigningJob: userSignedTxSigningJobs.get("directSplit"),
19981
+ nodeTxSigningJob: userSignedTxSigningJobs.get("node"),
19982
+ directNodeTxSigningJob: userSignedTxSigningJobs.get("directNode"),
19983
+ refundTxSigningJob: userSignedTxSigningJobs.get("cpfp"),
19984
+ directRefundTxSigningJob: userSignedTxSigningJobs.get("direct"),
19985
+ directFromCpfpRefundTxSigningJob: userSignedTxSigningJobs.get("directFromCpfp")
19986
+ }
19987
+ }
19988
+ });
19989
+ if (response.renewResult?.$case !== "renewNodeTimelockResult" || !response.renewResult?.renewNodeTimelockResult.node) {
19990
+ throw new ValidationError("Unexpected renew result", {
19991
+ field: "renewResult",
19992
+ value: response.renewResult
19706
19993
  });
19707
19994
  }
19708
- let { nextSequence, nextDirectSequence } = getNextTransactionSequence(
19709
- currSequence,
19710
- true
19995
+ return response.renewResult.renewNodeTimelockResult.node;
19996
+ }
19997
+ async createRenewRefundSigningJobs(node, parentNode) {
19998
+ const signingJobs = [];
19999
+ const parentTx = getTxFromRawTxBytes(parentNode.nodeTx);
20000
+ const parentNodeOutput = getTxFromRawTxBytes(parentNode.nodeTx).getOutput(
20001
+ 0
19711
20002
  );
19712
- const output = {
20003
+ if (!parentNodeOutput) {
20004
+ throw new Error("Parent node output not found");
20005
+ }
20006
+ const unsignedParentNodeOutput = {
19713
20007
  script: parentNodeOutput.script,
19714
20008
  amount: parentNodeOutput.amount
19715
20009
  };
19716
- const newNodeInput = {
19717
- txid: nodeInput.txid,
19718
- index: nodeInput.index,
19719
- sequence: useTestUnilateralSequence ? TEST_UNILATERAL_SEQUENCE : nextSequence
19720
- };
19721
- const newDirectInput = directNodeTx && directNodeInput ? {
19722
- txid: directNodeInput.txid,
19723
- index: directNodeInput.index,
19724
- sequence: useTestUnilateralSequence ? TEST_UNILATERAL_DIRECT_SEQUENCE : nextDirectSequence
19725
- } : void 0;
19726
- const { cpfpNodeTx, directNodeTx: newDirectNodeTx } = createNodeTxs(
19727
- output,
19728
- newNodeInput,
19729
- newDirectInput
19730
- );
19731
- const newCpfpNodeOutput = cpfpNodeTx.getOutput(0);
19732
- if (!newCpfpNodeOutput) {
19733
- throw Error("Could not get new cpfp node output");
19734
- }
19735
- const newDirectNodeOutput = newDirectNodeTx?.getOutput(0);
19736
- const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation({
20010
+ const keyDerivation = {
19737
20011
  type: "leaf" /* LEAF */,
19738
20012
  path: node.id
19739
- });
20013
+ };
20014
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
20015
+ const nodeTx = getTxFromRawTxBytes(node.nodeTx);
20016
+ const refundTx = getTxFromRawTxBytes(node.refundTx);
20017
+ const { nodeTx: newNodeTx, directNodeTx: newDirectNodeTx } = createDecrementedTimelockNodeTx(parentTx, nodeTx);
19740
20018
  signingJobs.push({
19741
20019
  signingPublicKey,
19742
- rawTx: cpfpNodeTx.toBytes(),
20020
+ rawTx: newNodeTx.toBytes(),
19743
20021
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
19744
20022
  type: "node",
19745
- parentTxOut: parentNodeOutput
20023
+ parentTxOut: unsignedParentNodeOutput,
20024
+ leafId: node.id,
20025
+ keyDerivation: {
20026
+ type: "leaf" /* LEAF */,
20027
+ path: node.id
20028
+ },
20029
+ verifyingKey: node.verifyingPublicKey
19746
20030
  });
19747
20031
  if (newDirectNodeTx) {
19748
20032
  signingJobs.push({
@@ -19750,537 +20034,299 @@ var TransferService = class extends BaseTransferService {
19750
20034
  rawTx: newDirectNodeTx.toBytes(),
19751
20035
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
19752
20036
  type: "directNode",
19753
- parentTxOut: parentNodeOutput
20037
+ parentTxOut: unsignedParentNodeOutput,
20038
+ leafId: node.id,
20039
+ keyDerivation: {
20040
+ type: "leaf" /* LEAF */,
20041
+ path: node.id
20042
+ },
20043
+ verifyingKey: node.verifyingPublicKey
19754
20044
  });
19755
20045
  }
19756
- const newCpfpRefundOutPoint = {
19757
- txid: hexToBytes3(getTxId(cpfpNodeTx)),
19758
- index: 0
19759
- };
19760
- let newDirectRefundOutPoint;
19761
- if (newDirectNodeTx) {
19762
- newDirectRefundOutPoint = {
19763
- txid: hexToBytes3(getTxId(newDirectNodeTx)),
19764
- index: 0
19765
- };
20046
+ const newCpfpNodeOutput = newNodeTx.getOutput(0);
20047
+ if (!newCpfpNodeOutput) {
20048
+ throw Error("Could not get new cpfp node output");
19766
20049
  }
19767
- const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
19768
- sequence: INITIAL_SEQUENCE,
19769
- directSequence: INITIAL_DIRECT_SEQUENCE,
19770
- input: newCpfpRefundOutPoint,
19771
- directInput: newDirectRefundOutPoint,
19772
- amountSats: nodeOutput.amount,
19773
- receivingPubkey: await this.config.signer.getPublicKeyFromDerivation({
19774
- type: "leaf" /* LEAF */,
19775
- path: node.id
19776
- }),
20050
+ const newDirectNodeOutput = newDirectNodeTx?.getOutput(0);
20051
+ const amountSats = refundTx.getOutput(0).amount;
20052
+ if (amountSats === void 0) {
20053
+ throw new Error("Amount not found in extendTimelock");
20054
+ }
20055
+ const directAmountSats = newDirectNodeOutput?.amount;
20056
+ if (directAmountSats === void 0) {
20057
+ throw new Error("Amount not found in extendTimelock");
20058
+ }
20059
+ const {
20060
+ cpfpRefundTx: newRefundTx,
20061
+ directRefundTx: newDirectRefundTx,
20062
+ directFromCpfpRefundTx: newDirectFromCpfpRefundTx
20063
+ } = createInitialTimelockRefundTxs({
20064
+ nodeTx: newNodeTx,
20065
+ directNodeTx: newDirectNodeTx,
20066
+ receivingPubkey: signingPublicKey,
19777
20067
  network: this.config.getNetwork()
19778
20068
  });
19779
20069
  signingJobs.push({
19780
20070
  signingPublicKey,
19781
- rawTx: cpfpRefundTx.toBytes(),
20071
+ rawTx: newRefundTx.toBytes(),
19782
20072
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
19783
20073
  type: "cpfp",
19784
- parentTxOut: newCpfpNodeOutput
20074
+ parentTxOut: newCpfpNodeOutput,
20075
+ leafId: node.id,
20076
+ keyDerivation,
20077
+ verifyingKey: node.verifyingPublicKey
19785
20078
  });
19786
- if (directRefundTx && newDirectNodeOutput) {
20079
+ if (newDirectRefundTx && newDirectNodeOutput) {
19787
20080
  signingJobs.push({
19788
20081
  signingPublicKey,
19789
- rawTx: directRefundTx.toBytes(),
20082
+ rawTx: newDirectRefundTx.toBytes(),
19790
20083
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
19791
20084
  type: "direct",
19792
- parentTxOut: newDirectNodeOutput
20085
+ parentTxOut: newDirectNodeOutput,
20086
+ leafId: node.id,
20087
+ keyDerivation,
20088
+ verifyingKey: node.verifyingPublicKey
19793
20089
  });
19794
20090
  }
19795
- if (directFromCpfpRefundTx && newCpfpNodeOutput) {
20091
+ if (newDirectFromCpfpRefundTx && newDirectNodeOutput) {
19796
20092
  signingJobs.push({
19797
20093
  signingPublicKey,
19798
- rawTx: directFromCpfpRefundTx.toBytes(),
20094
+ rawTx: newDirectFromCpfpRefundTx.toBytes(),
19799
20095
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
19800
20096
  type: "directFromCpfp",
19801
- parentTxOut: newCpfpNodeOutput
20097
+ parentTxOut: newCpfpNodeOutput,
20098
+ leafId: node.id,
20099
+ keyDerivation,
20100
+ verifyingKey: node.verifyingPublicKey
19802
20101
  });
19803
20102
  }
19804
- const sparkClient = await this.connectionManager.createSparkClient(
19805
- this.config.getCoordinatorAddress()
19806
- );
19807
- const response = await sparkClient.refresh_timelock_v2({
19808
- leafId: node.id,
19809
- ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
19810
- signingJobs: signingJobs.map(getSigningJobProto)
19811
- });
19812
- if (signingJobs.length !== response.signingResults.length) {
19813
- throw Error(
19814
- `number of signing jobs and signing results do not match: ${signingJobs.length} !== ${response.signingResults.length}`
19815
- );
19816
- }
19817
- let nodeSignatures = [];
19818
- let leafCpfpSignature;
19819
- let leafDirectSignature;
19820
- let cpfpRefundSignature;
19821
- let directRefundSignature;
19822
- let directFromCpfpRefundSignature;
19823
- for (const [i, signingResult] of response.signingResults.entries()) {
19824
- const signingJob = signingJobs[i];
19825
- if (!signingJob || !signingResult) {
19826
- throw Error("Signing job does not exist");
19827
- }
19828
- const rawTx = getTxFromRawTxBytes(signingJob.rawTx);
19829
- const txOut = signingJob.parentTxOut;
19830
- if (!txOut) {
19831
- throw Error("Could not get tx out");
19832
- }
19833
- const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
19834
- const userSignature = await this.config.signer.signFrost({
19835
- message: rawTxSighash,
19836
- keyDerivation: {
19837
- type: "leaf" /* LEAF */,
19838
- path: node.id
19839
- },
19840
- publicKey: signingJob.signingPublicKey,
19841
- verifyingKey: signingResult.verifyingKey,
19842
- selfCommitment: signingJob.signingNonceCommitment,
19843
- statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
19844
- adaptorPubKey: new Uint8Array()
19845
- });
19846
- const signature = await this.config.signer.aggregateFrost({
19847
- message: rawTxSighash,
19848
- statechainSignatures: signingResult.signingResult?.signatureShares,
19849
- statechainPublicKeys: signingResult.signingResult?.publicKeys,
19850
- verifyingKey: signingResult.verifyingKey,
19851
- statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
19852
- selfCommitment: signingJob.signingNonceCommitment,
19853
- publicKey: signingJob.signingPublicKey,
19854
- selfSignature: userSignature,
19855
- adaptorPubKey: new Uint8Array()
19856
- });
19857
- if (signingJob.type === "node") {
19858
- leafCpfpSignature = signature;
19859
- } else if (signingJob.type === "directNode") {
19860
- leafDirectSignature = signature;
19861
- } else if (signingJob.type === "cpfp") {
19862
- cpfpRefundSignature = signature;
19863
- } else if (signingJob.type === "direct") {
19864
- directRefundSignature = signature;
19865
- } else if (signingJob.type === "directFromCpfp") {
19866
- directFromCpfpRefundSignature = signature;
19867
- }
19868
- }
19869
- nodeSignatures.push({
19870
- nodeId: node.id,
19871
- nodeTxSignature: leafCpfpSignature || new Uint8Array(),
19872
- directNodeTxSignature: leafDirectSignature || new Uint8Array(),
19873
- refundTxSignature: cpfpRefundSignature || new Uint8Array(),
19874
- directRefundTxSignature: directRefundSignature || new Uint8Array(),
19875
- directFromCpfpRefundTxSignature: directFromCpfpRefundSignature || new Uint8Array()
19876
- });
19877
- const result = await sparkClient.finalize_node_signatures_v2({
19878
- intent: 3 /* REFRESH */,
19879
- nodeSignatures
19880
- });
19881
- return result;
19882
- }
19883
- async refreshTimelockNodes(node, parentNode) {
19884
- return await this.refreshTimelockNodesInternal(node, parentNode);
20103
+ return signingJobs;
19885
20104
  }
19886
- async extendTimelock(node) {
19887
- const nodeTx = getTxFromRawTxBytes(node.nodeTx);
19888
- const refundTx = getTxFromRawTxBytes(node.refundTx);
19889
- const refundSequence = refundTx.getInput(0).sequence || 0;
19890
- const newNodeOutPoint = {
19891
- txid: hexToBytes3(getTxId(nodeTx)),
19892
- index: 0
19893
- };
19894
- const {
19895
- nextSequence: newNodeSequence,
19896
- nextDirectSequence: newDirectNodeSequence
19897
- } = getNextTransactionSequence(refundSequence);
19898
- const newNodeTx = new Transaction3({
19899
- version: 3,
19900
- allowUnknownOutputs: true
19901
- });
19902
- newNodeTx.addInput({ ...newNodeOutPoint, sequence: newNodeSequence });
19903
- const originalOutput = nodeTx.getOutput(0);
19904
- if (!originalOutput) {
19905
- throw Error("Could not get original node output");
19906
- }
19907
- newNodeTx.addOutput({
19908
- script: originalOutput.script,
19909
- amount: originalOutput.amount
19910
- });
19911
- newNodeTx.addOutput(getEphemeralAnchorOutput());
19912
- let newDirectNodeTx;
19913
- if (node.directTx.length > 0) {
19914
- newDirectNodeTx = new Transaction3({
19915
- version: 3,
19916
- allowUnknownOutputs: true
19917
- });
19918
- newDirectNodeTx.addInput({
19919
- ...newNodeOutPoint,
19920
- sequence: newDirectNodeSequence
19921
- });
19922
- newDirectNodeTx.addOutput({
19923
- script: originalOutput.script,
19924
- amount: maybeApplyFee(originalOutput.amount)
19925
- });
19926
- }
19927
- const newCpfpRefundOutPoint = {
19928
- txid: hexToBytes3(getTxId(newNodeTx)),
19929
- index: 0
19930
- };
19931
- let newDirectRefundOutPoint;
19932
- if (newDirectNodeTx) {
19933
- newDirectRefundOutPoint = {
19934
- txid: hexToBytes3(getTxId(newDirectNodeTx)),
19935
- index: 0
19936
- };
19937
- }
19938
- const amountSats = refundTx.getOutput(0).amount;
19939
- if (amountSats === void 0) {
19940
- throw new Error("Amount not found in extendTimelock");
19941
- }
19942
- const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation({
19943
- type: "leaf" /* LEAF */,
19944
- path: node.id
19945
- });
19946
- const {
19947
- cpfpRefundTx: newCpfpRefundTx,
19948
- directRefundTx: newDirectRefundTx,
19949
- directFromCpfpRefundTx: newDirectFromCpfpRefundTx
19950
- } = createRefundTxs({
19951
- sequence: INITIAL_SEQUENCE,
19952
- directSequence: INITIAL_DIRECT_SEQUENCE,
19953
- input: newCpfpRefundOutPoint,
19954
- directInput: newDirectRefundOutPoint,
19955
- amountSats,
19956
- receivingPubkey: signingPublicKey,
19957
- network: this.config.getNetwork()
19958
- });
19959
- if (!newCpfpRefundTx) {
19960
- throw new ValidationError(
19961
- "Failed to create refund transactions in extendTimelock"
19962
- );
19963
- }
19964
- const nodeSighash = getSigHashFromTx(newNodeTx, 0, nodeTx.getOutput(0));
19965
- const directNodeSighash = newDirectNodeTx ? getSigHashFromTx(newDirectNodeTx, 0, nodeTx.getOutput(0)) : void 0;
19966
- const cpfpRefundSighash = getSigHashFromTx(
19967
- newCpfpRefundTx,
19968
- 0,
19969
- newNodeTx.getOutput(0)
20105
+ async createRenewNodeSigningJobs(node, parentNode) {
20106
+ const signingJobs = [];
20107
+ const parentTx = getTxFromRawTxBytes(parentNode.nodeTx);
20108
+ const parentNodeOutput = getTxFromRawTxBytes(parentNode.nodeTx).getOutput(
20109
+ 0
19970
20110
  );
19971
- const directRefundSighash = newDirectNodeTx && newDirectRefundTx ? getSigHashFromTx(newDirectRefundTx, 0, newDirectNodeTx.getOutput(0)) : void 0;
19972
- const directFromCpfpRefundSighash = newDirectFromCpfpRefundTx ? getSigHashFromTx(newDirectFromCpfpRefundTx, 0, newNodeTx.getOutput(0)) : void 0;
19973
- const newNodeSigningJob = {
19974
- signingPublicKey,
19975
- rawTx: newNodeTx.toBytes(),
19976
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
19977
- };
19978
- const newDirectNodeSigningJob = newDirectNodeTx ? {
19979
- signingPublicKey,
19980
- rawTx: newDirectNodeTx.toBytes(),
19981
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
19982
- } : void 0;
19983
- const newCpfpRefundSigningJob = {
19984
- signingPublicKey,
19985
- rawTx: newCpfpRefundTx.toBytes(),
19986
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
20111
+ const unsignedParentNodeOutput = {
20112
+ script: parentNodeOutput.script,
20113
+ amount: parentNodeOutput.amount
19987
20114
  };
19988
- const newDirectRefundSigningJob = newDirectRefundTx ? {
19989
- signingPublicKey,
19990
- rawTx: newDirectRefundTx.toBytes(),
19991
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
19992
- } : void 0;
19993
- const newDirectFromCpfpRefundSigningJob = newDirectFromCpfpRefundTx ? {
19994
- signingPublicKey,
19995
- rawTx: newDirectFromCpfpRefundTx.toBytes(),
19996
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
19997
- } : void 0;
19998
- const sparkClient = await this.connectionManager.createSparkClient(
19999
- this.config.getCoordinatorAddress()
20000
- );
20001
- const response = await sparkClient.extend_leaf_v2({
20002
- leafId: node.id,
20003
- ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
20004
- nodeTxSigningJob: getSigningJobProto(newNodeSigningJob),
20005
- directNodeTxSigningJob: newDirectNodeSigningJob ? getSigningJobProto(newDirectNodeSigningJob) : void 0,
20006
- refundTxSigningJob: getSigningJobProto(newCpfpRefundSigningJob),
20007
- directRefundTxSigningJob: newDirectRefundSigningJob ? getSigningJobProto(newDirectRefundSigningJob) : void 0,
20008
- directFromCpfpRefundTxSigningJob: newDirectFromCpfpRefundSigningJob ? getSigningJobProto(newDirectFromCpfpRefundSigningJob) : void 0
20009
- });
20010
- if (!response.nodeTxSigningResult || !response.refundTxSigningResult) {
20011
- throw new Error("Signing result does not exist");
20012
- }
20013
20115
  const keyDerivation = {
20014
20116
  type: "leaf" /* LEAF */,
20015
20117
  path: node.id
20016
20118
  };
20017
- const nodeUserSig = await this.config.signer.signFrost({
20018
- message: nodeSighash,
20119
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
20120
+ const { nodeTx: splitNodeTx, directNodeTx: splitNodeDirectTx } = createZeroTimelockNodeTx(parentTx);
20121
+ signingJobs.push({
20122
+ signingPublicKey,
20123
+ rawTx: splitNodeTx.toBytes(),
20124
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
20125
+ type: "split",
20126
+ parentTxOut: unsignedParentNodeOutput,
20127
+ leafId: node.id,
20019
20128
  keyDerivation,
20020
- publicKey: signingPublicKey,
20021
- verifyingKey: response.nodeTxSigningResult.verifyingKey,
20022
- selfCommitment: newNodeSigningJob.signingNonceCommitment,
20023
- statechainCommitments: response.nodeTxSigningResult.signingResult?.signingNonceCommitments,
20024
- adaptorPubKey: new Uint8Array()
20025
- });
20026
- const nodeSig = await this.config.signer.aggregateFrost({
20027
- message: nodeSighash,
20028
- statechainSignatures: response.nodeTxSigningResult.signingResult?.signatureShares,
20029
- statechainPublicKeys: response.nodeTxSigningResult.signingResult?.publicKeys,
20030
- verifyingKey: response.nodeTxSigningResult.verifyingKey,
20031
- statechainCommitments: response.nodeTxSigningResult.signingResult?.signingNonceCommitments,
20032
- selfCommitment: newNodeSigningJob.signingNonceCommitment,
20033
- publicKey: signingPublicKey,
20034
- selfSignature: nodeUserSig,
20035
- adaptorPubKey: new Uint8Array()
20129
+ verifyingKey: node.verifyingPublicKey
20036
20130
  });
20037
- let directNodeSig;
20038
- if (directNodeSighash && newDirectNodeSigningJob && response.directNodeTxSigningResult) {
20039
- const directNodeUserSig = await this.config.signer.signFrost({
20040
- message: directNodeSighash,
20131
+ if (splitNodeDirectTx) {
20132
+ signingJobs.push({
20133
+ signingPublicKey,
20134
+ rawTx: splitNodeDirectTx.toBytes(),
20135
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
20136
+ type: "directSplit",
20137
+ parentTxOut: unsignedParentNodeOutput,
20138
+ leafId: node.id,
20041
20139
  keyDerivation,
20042
- publicKey: signingPublicKey,
20043
- verifyingKey: response.directNodeTxSigningResult.verifyingKey,
20044
- selfCommitment: newDirectNodeSigningJob.signingNonceCommitment,
20045
- statechainCommitments: response.directNodeTxSigningResult.signingResult?.signingNonceCommitments,
20046
- adaptorPubKey: new Uint8Array()
20047
- });
20048
- directNodeSig = await this.config.signer.aggregateFrost({
20049
- message: directNodeSighash,
20050
- statechainSignatures: response.directNodeTxSigningResult.signingResult?.signatureShares,
20051
- statechainPublicKeys: response.directNodeTxSigningResult.signingResult?.publicKeys,
20052
- verifyingKey: response.directNodeTxSigningResult.verifyingKey,
20053
- statechainCommitments: response.directNodeTxSigningResult.signingResult?.signingNonceCommitments,
20054
- selfCommitment: newDirectNodeSigningJob.signingNonceCommitment,
20055
- publicKey: signingPublicKey,
20056
- selfSignature: directNodeUserSig,
20057
- adaptorPubKey: new Uint8Array()
20140
+ verifyingKey: node.verifyingPublicKey
20058
20141
  });
20059
20142
  }
20060
- const cpfpRefundUserSig = await this.config.signer.signFrost({
20061
- message: cpfpRefundSighash,
20143
+ const splitNodeOutput = splitNodeTx.getOutput(0);
20144
+ const splitNodeDirectOutput = splitNodeDirectTx.getOutput(0);
20145
+ if (!splitNodeDirectOutput.amount || !splitNodeDirectOutput.script) {
20146
+ throw new Error("Could not get split node output");
20147
+ }
20148
+ const unsignedSplitNodeOutput = {
20149
+ script: splitNodeDirectOutput.script,
20150
+ amount: splitNodeDirectOutput.amount
20151
+ };
20152
+ const { nodeTx: newNodeTx, directNodeTx: newDirectNodeTx } = createInitialTimelockNodeTx(splitNodeTx);
20153
+ signingJobs.push({
20154
+ signingPublicKey,
20155
+ rawTx: newNodeTx.toBytes(),
20156
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
20157
+ type: "node",
20158
+ parentTxOut: splitNodeOutput,
20159
+ leafId: node.id,
20062
20160
  keyDerivation,
20063
- publicKey: signingPublicKey,
20064
- verifyingKey: response.refundTxSigningResult.verifyingKey,
20065
- selfCommitment: newCpfpRefundSigningJob.signingNonceCommitment,
20066
- statechainCommitments: response.refundTxSigningResult.signingResult?.signingNonceCommitments,
20067
- adaptorPubKey: new Uint8Array()
20161
+ verifyingKey: node.verifyingPublicKey
20068
20162
  });
20069
- const cpfpRefundSig = await this.config.signer.aggregateFrost({
20070
- message: cpfpRefundSighash,
20071
- statechainSignatures: response.refundTxSigningResult.signingResult?.signatureShares,
20072
- statechainPublicKeys: response.refundTxSigningResult.signingResult?.publicKeys,
20073
- verifyingKey: response.refundTxSigningResult.verifyingKey,
20074
- statechainCommitments: response.refundTxSigningResult.signingResult?.signingNonceCommitments,
20075
- selfCommitment: newCpfpRefundSigningJob.signingNonceCommitment,
20076
- publicKey: signingPublicKey,
20077
- selfSignature: cpfpRefundUserSig,
20078
- adaptorPubKey: new Uint8Array()
20079
- });
20080
- let directRefundSig;
20081
- if (directRefundSighash && newDirectRefundSigningJob && response.directRefundTxSigningResult) {
20082
- const directRefundUserSig = await this.config.signer.signFrost({
20083
- message: directRefundSighash,
20084
- keyDerivation,
20085
- publicKey: signingPublicKey,
20086
- verifyingKey: response.directRefundTxSigningResult.verifyingKey,
20087
- selfCommitment: newDirectRefundSigningJob.signingNonceCommitment,
20088
- statechainCommitments: response.directRefundTxSigningResult.signingResult?.signingNonceCommitments,
20089
- adaptorPubKey: new Uint8Array()
20090
- });
20091
- directRefundSig = await this.config.signer.aggregateFrost({
20092
- message: directRefundSighash,
20093
- statechainSignatures: response.directRefundTxSigningResult.signingResult?.signatureShares,
20094
- statechainPublicKeys: response.directRefundTxSigningResult.signingResult?.publicKeys,
20095
- verifyingKey: response.directRefundTxSigningResult.verifyingKey,
20096
- statechainCommitments: response.directRefundTxSigningResult.signingResult?.signingNonceCommitments,
20097
- selfCommitment: newDirectRefundSigningJob.signingNonceCommitment,
20098
- publicKey: signingPublicKey,
20099
- selfSignature: directRefundUserSig,
20100
- adaptorPubKey: new Uint8Array()
20101
- });
20102
- }
20103
- let directFromCpfpRefundSig;
20104
- if (directFromCpfpRefundSighash && newDirectFromCpfpRefundSigningJob && response.directFromCpfpRefundTxSigningResult) {
20105
- const directFromCpfpRefundUserSig = await this.config.signer.signFrost({
20106
- message: directFromCpfpRefundSighash,
20163
+ if (newDirectNodeTx) {
20164
+ signingJobs.push({
20165
+ signingPublicKey,
20166
+ rawTx: newDirectNodeTx.toBytes(),
20167
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
20168
+ type: "directNode",
20169
+ parentTxOut: splitNodeOutput,
20170
+ leafId: node.id,
20107
20171
  keyDerivation,
20108
- publicKey: signingPublicKey,
20109
- verifyingKey: response.directFromCpfpRefundTxSigningResult.verifyingKey,
20110
- selfCommitment: newDirectFromCpfpRefundSigningJob.signingNonceCommitment,
20111
- statechainCommitments: response.directFromCpfpRefundTxSigningResult.signingResult?.signingNonceCommitments,
20112
- adaptorPubKey: new Uint8Array()
20113
- });
20114
- directFromCpfpRefundSig = await this.config.signer.aggregateFrost({
20115
- message: directFromCpfpRefundSighash,
20116
- statechainSignatures: response.directFromCpfpRefundTxSigningResult.signingResult?.signatureShares,
20117
- statechainPublicKeys: response.directFromCpfpRefundTxSigningResult.signingResult?.publicKeys,
20118
- verifyingKey: response.directFromCpfpRefundTxSigningResult.verifyingKey,
20119
- statechainCommitments: response.directFromCpfpRefundTxSigningResult.signingResult?.signingNonceCommitments,
20120
- selfCommitment: newDirectFromCpfpRefundSigningJob.signingNonceCommitment,
20121
- publicKey: signingPublicKey,
20122
- selfSignature: directFromCpfpRefundUserSig,
20123
- adaptorPubKey: new Uint8Array()
20124
- });
20125
- }
20126
- return await sparkClient.finalize_node_signatures_v2({
20127
- intent: 4 /* EXTEND */,
20128
- nodeSignatures: [
20129
- {
20130
- nodeId: response.leafId,
20131
- nodeTxSignature: nodeSig,
20132
- directNodeTxSignature: directNodeSig,
20133
- refundTxSignature: cpfpRefundSig,
20134
- directRefundTxSignature: directRefundSig,
20135
- directFromCpfpRefundTxSignature: directFromCpfpRefundSig
20136
- }
20137
- ]
20138
- });
20139
- }
20140
- async testonly_expireTimeLockNodeTx(node, parentNode) {
20141
- return await this.refreshTimelockNodesInternal(node, parentNode, true);
20142
- }
20143
- async testonly_expireTimeLockRefundtx(node) {
20144
- const nodeTx = getTxFromRawTxBytes(node.nodeTx);
20145
- const directNodeTx = node.directTx.length > 0 ? getTxFromRawTxBytes(node.directTx) : void 0;
20146
- const cpfpRefundTx = getTxFromRawTxBytes(node.refundTx);
20147
- const currSequence = cpfpRefundTx.getInput(0).sequence || 0;
20148
- const currTimelock = getCurrentTimelock(currSequence);
20149
- if (currTimelock <= 100) {
20150
- throw new ValidationError("Cannot expire timelock below 100", {
20151
- field: "currTimelock",
20152
- value: currTimelock,
20153
- expected: "Timelock greater than 100"
20172
+ verifyingKey: node.verifyingPublicKey
20154
20173
  });
20155
20174
  }
20156
- const nextSequence = TEST_UNILATERAL_SEQUENCE;
20157
- const nextDirectSequence = TEST_UNILATERAL_SEQUENCE + DIRECT_TIMELOCK_OFFSET;
20158
- const nodeOutput = nodeTx.getOutput(0);
20159
- if (!nodeOutput) {
20160
- throw Error("Could not get node output");
20161
- }
20162
- const keyDerivation = {
20163
- type: "leaf" /* LEAF */,
20164
- path: node.id
20165
- };
20166
- const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
20167
- const cpfpRefundOutPoint = {
20168
- txid: hexToBytes3(getTxId(nodeTx)),
20169
- index: 0
20170
- };
20171
- let directRefundOutPoint;
20172
- if (directNodeTx) {
20173
- directRefundOutPoint = {
20174
- txid: hexToBytes3(getTxId(directNodeTx)),
20175
- index: 0
20176
- };
20175
+ const newCpfpNodeOutput = newNodeTx.getOutput(0);
20176
+ if (!newCpfpNodeOutput) {
20177
+ throw Error("Could not get new cpfp node output");
20177
20178
  }
20179
+ const newDirectNodeOutput = newDirectNodeTx?.getOutput(0);
20178
20180
  const {
20179
- cpfpRefundTx: newCpfpRefundTx,
20181
+ cpfpRefundTx: newRefundTx,
20180
20182
  directRefundTx: newDirectRefundTx,
20181
20183
  directFromCpfpRefundTx: newDirectFromCpfpRefundTx
20182
- } = createRefundTxs({
20183
- sequence: nextSequence,
20184
- directSequence: nextDirectSequence,
20185
- input: cpfpRefundOutPoint,
20186
- directInput: directRefundOutPoint,
20187
- amountSats: nodeOutput.amount,
20184
+ } = createInitialTimelockRefundTxs({
20185
+ nodeTx: newNodeTx,
20186
+ directNodeTx: newDirectNodeTx,
20188
20187
  receivingPubkey: signingPublicKey,
20189
20188
  network: this.config.getNetwork()
20190
20189
  });
20191
- const signingJobs = [];
20192
20190
  signingJobs.push({
20193
20191
  signingPublicKey,
20194
- rawTx: newCpfpRefundTx.toBytes(),
20192
+ rawTx: newRefundTx.toBytes(),
20195
20193
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
20196
20194
  type: "cpfp",
20197
- parentTxOut: nodeOutput
20195
+ parentTxOut: newCpfpNodeOutput,
20196
+ leafId: node.id,
20197
+ keyDerivation,
20198
+ verifyingKey: node.verifyingPublicKey
20198
20199
  });
20199
- const directNodeTxOut = directNodeTx?.getOutput(0);
20200
- if (newDirectRefundTx && directNodeTxOut) {
20200
+ if (newDirectRefundTx && newDirectNodeOutput) {
20201
20201
  signingJobs.push({
20202
20202
  signingPublicKey,
20203
20203
  rawTx: newDirectRefundTx.toBytes(),
20204
20204
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
20205
20205
  type: "direct",
20206
- parentTxOut: directNodeTxOut
20206
+ parentTxOut: newDirectNodeOutput,
20207
+ leafId: node.id,
20208
+ keyDerivation,
20209
+ verifyingKey: node.verifyingPublicKey
20207
20210
  });
20208
20211
  }
20209
- if (newDirectFromCpfpRefundTx) {
20212
+ if (newDirectFromCpfpRefundTx && newDirectNodeOutput) {
20210
20213
  signingJobs.push({
20211
20214
  signingPublicKey,
20212
20215
  rawTx: newDirectFromCpfpRefundTx.toBytes(),
20213
20216
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
20214
20217
  type: "directFromCpfp",
20215
- parentTxOut: nodeOutput
20218
+ parentTxOut: newCpfpNodeOutput,
20219
+ leafId: node.id,
20220
+ keyDerivation,
20221
+ verifyingKey: node.verifyingPublicKey
20216
20222
  });
20217
20223
  }
20224
+ return signingJobs;
20225
+ }
20226
+ async renewZeroTimelockNodeTxn(node) {
20218
20227
  const sparkClient = await this.connectionManager.createSparkClient(
20219
20228
  this.config.getCoordinatorAddress()
20220
20229
  );
20221
- const response = await sparkClient.refresh_timelock_v2({
20222
- leafId: node.id,
20223
- ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
20224
- signingJobs: signingJobs.map(getSigningJobProto)
20230
+ const signingJobs = await this.createRenewZeroTimelockNodeSigningJobs(node);
20231
+ const statechainCommitments = await sparkClient.get_signing_commitments({
20232
+ nodeIds: [node.id],
20233
+ count: signingJobs.length
20225
20234
  });
20226
- if (response.signingResults.length !== signingJobs.length) {
20227
- throw Error(
20228
- `Expected ${signingJobs.length} signing results, got ${response.signingResults.length}`
20229
- );
20230
- }
20231
- let cpfpRefundSignature;
20232
- let directRefundSignature;
20233
- let directFromCpfpRefundSignature;
20234
- for (const [i, signingJob] of signingJobs.entries()) {
20235
- const signingResult = response.signingResults[i];
20236
- if (!signingResult) {
20237
- throw Error("Signing result does not exist");
20235
+ const mappedSigningJobs = signingJobs.map((signingJob, index) => {
20236
+ const signingNonceCommitments = statechainCommitments.signingCommitments[index]?.signingNonceCommitments;
20237
+ if (!signingNonceCommitments) {
20238
+ throw new ValidationError("Signing nonce commitments not found", {
20239
+ field: "signingNonceCommitments",
20240
+ value: signingNonceCommitments
20241
+ });
20238
20242
  }
20239
- const rawTx = getTxFromRawTxBytes(signingJob.rawTx);
20240
- const txOut = signingJob.parentTxOut;
20241
- const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
20242
- const userSignature = await this.config.signer.signFrost({
20243
- message: rawTxSighash,
20244
- keyDerivation,
20245
- publicKey: signingPublicKey,
20246
- verifyingKey: signingResult.verifyingKey,
20247
- selfCommitment: signingJob.signingNonceCommitment,
20248
- statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
20249
- adaptorPubKey: new Uint8Array()
20250
- });
20251
- const signature = await this.config.signer.aggregateFrost({
20252
- message: rawTxSighash,
20253
- statechainSignatures: signingResult.signingResult?.signatureShares,
20254
- statechainPublicKeys: signingResult.signingResult?.publicKeys,
20255
- verifyingKey: signingResult.verifyingKey,
20256
- statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
20257
- selfCommitment: signingJob.signingNonceCommitment,
20258
- publicKey: signingPublicKey,
20259
- selfSignature: userSignature,
20260
- adaptorPubKey: new Uint8Array()
20261
- });
20262
- if (signingJob.type === "cpfp") {
20263
- cpfpRefundSignature = signature;
20264
- } else if (signingJob.type === "direct") {
20265
- directRefundSignature = signature;
20266
- } else if (signingJob.type === "directFromCpfp") {
20267
- directFromCpfpRefundSignature = signature;
20243
+ return {
20244
+ ...signingJob,
20245
+ signingNonceCommitments
20246
+ };
20247
+ });
20248
+ const userSignedTxSigningJobs = await this.signingService.signSigningJobs(mappedSigningJobs);
20249
+ const renewZeroTimelockNodeSigningJob = {
20250
+ nodeTxSigningJob: userSignedTxSigningJobs.get("node"),
20251
+ refundTxSigningJob: userSignedTxSigningJobs.get("cpfp"),
20252
+ directNodeTxSigningJob: userSignedTxSigningJobs.get("directNode"),
20253
+ directRefundTxSigningJob: void 0,
20254
+ directFromCpfpRefundTxSigningJob: userSignedTxSigningJobs.get("directFromCpfp")
20255
+ };
20256
+ const response = await sparkClient.renew_leaf({
20257
+ leafId: node.id,
20258
+ signingJobs: {
20259
+ $case: "renewNodeZeroTimelockSigningJob",
20260
+ renewNodeZeroTimelockSigningJob: renewZeroTimelockNodeSigningJob
20268
20261
  }
20262
+ });
20263
+ if (response.renewResult?.$case !== "renewNodeZeroTimelockResult" || !response.renewResult?.renewNodeZeroTimelockResult.node) {
20264
+ throw new ValidationError("Unexpected renew result", {
20265
+ field: "renewResult",
20266
+ value: response.renewResult
20267
+ });
20269
20268
  }
20270
- const result = await sparkClient.finalize_node_signatures_v2({
20271
- intent: 3 /* REFRESH */,
20272
- nodeSignatures: [
20273
- {
20274
- nodeId: node.id,
20275
- nodeTxSignature: new Uint8Array(),
20276
- directNodeTxSignature: new Uint8Array(),
20277
- refundTxSignature: cpfpRefundSignature,
20278
- directRefundTxSignature: directRefundSignature,
20279
- directFromCpfpRefundTxSignature: directFromCpfpRefundSignature
20280
- }
20281
- ]
20269
+ return response.renewResult.renewNodeZeroTimelockResult.node;
20270
+ }
20271
+ async createRenewZeroTimelockNodeSigningJobs(node) {
20272
+ const signingJobs = [];
20273
+ const keyDerivation = {
20274
+ type: "leaf" /* LEAF */,
20275
+ path: node.id
20276
+ };
20277
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
20278
+ const nodeTx = getTxFromRawTxBytes(node.nodeTx);
20279
+ const { nodeTx: newNodeTx, directNodeTx: newDirectNodeTx } = createZeroTimelockNodeTx(nodeTx);
20280
+ signingJobs.push({
20281
+ signingPublicKey,
20282
+ rawTx: newNodeTx.toBytes(),
20283
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
20284
+ type: "node",
20285
+ parentTxOut: nodeTx.getOutput(0),
20286
+ leafId: node.id,
20287
+ keyDerivation,
20288
+ verifyingKey: node.verifyingPublicKey
20282
20289
  });
20283
- return result;
20290
+ signingJobs.push({
20291
+ signingPublicKey,
20292
+ rawTx: newDirectNodeTx.toBytes(),
20293
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
20294
+ type: "directNode",
20295
+ parentTxOut: nodeTx.getOutput(0),
20296
+ leafId: node.id,
20297
+ keyDerivation,
20298
+ verifyingKey: node.verifyingPublicKey
20299
+ });
20300
+ const { cpfpRefundTx, directFromCpfpRefundTx } = createInitialTimelockRefundTxs({
20301
+ nodeTx: newNodeTx,
20302
+ directNodeTx: newDirectNodeTx,
20303
+ receivingPubkey: signingPublicKey,
20304
+ network: this.config.getNetwork()
20305
+ });
20306
+ signingJobs.push({
20307
+ signingPublicKey,
20308
+ rawTx: cpfpRefundTx.toBytes(),
20309
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
20310
+ type: "cpfp",
20311
+ parentTxOut: newNodeTx.getOutput(0),
20312
+ leafId: node.id,
20313
+ keyDerivation,
20314
+ verifyingKey: node.verifyingPublicKey
20315
+ });
20316
+ if (!directFromCpfpRefundTx) {
20317
+ throw new Error("Could not create direct refund transactions");
20318
+ }
20319
+ signingJobs.push({
20320
+ signingPublicKey,
20321
+ rawTx: directFromCpfpRefundTx.toBytes(),
20322
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
20323
+ type: "directFromCpfp",
20324
+ parentTxOut: newNodeTx.getOutput(0),
20325
+ leafId: node.id,
20326
+ keyDerivation,
20327
+ verifyingKey: node.verifyingPublicKey
20328
+ });
20329
+ return signingJobs;
20284
20330
  }
20285
20331
  };
20286
20332
 
@@ -20322,71 +20368,6 @@ var CoopExitService = class extends BaseTransferService {
20322
20368
  directFromCpfpSignaturesMap
20323
20369
  };
20324
20370
  }
20325
- createConnectorRefundTransactions(sequence, directSequence, cpfpNodeOutPoint, directNodeOutPoint, connectorOutput, amountSats, receiverPubKey) {
20326
- const cpfpRefundTx = new Transaction4();
20327
- if (!cpfpNodeOutPoint.txid || cpfpNodeOutPoint.index === void 0) {
20328
- throw new ValidationError("Invalid CPFP node outpoint", {
20329
- field: "cpfpNodeOutPoint",
20330
- value: { txid: cpfpNodeOutPoint.txid, index: cpfpNodeOutPoint.index },
20331
- expected: "Both txid and index must be defined"
20332
- });
20333
- }
20334
- cpfpRefundTx.addInput({
20335
- txid: cpfpNodeOutPoint.txid,
20336
- index: cpfpNodeOutPoint.index,
20337
- sequence
20338
- });
20339
- cpfpRefundTx.addInput(connectorOutput);
20340
- const receiverScript = getP2TRScriptFromPublicKey(
20341
- receiverPubKey,
20342
- this.config.getNetwork()
20343
- );
20344
- cpfpRefundTx.addOutput({
20345
- script: receiverScript,
20346
- amount: amountSats
20347
- });
20348
- let directRefundTx;
20349
- let directFromCpfpRefundTx;
20350
- if (directNodeOutPoint) {
20351
- if (!directNodeOutPoint.txid || directNodeOutPoint.index === void 0) {
20352
- throw new ValidationError("Invalid direct node outpoint", {
20353
- field: "directNodeOutPoint",
20354
- value: {
20355
- txid: directNodeOutPoint.txid,
20356
- index: directNodeOutPoint.index
20357
- },
20358
- expected: "Both txid and index must be defined"
20359
- });
20360
- }
20361
- directRefundTx = new Transaction4();
20362
- directRefundTx.addInput({
20363
- txid: directNodeOutPoint.txid,
20364
- index: directNodeOutPoint.index,
20365
- sequence: directSequence
20366
- });
20367
- directRefundTx.addInput(connectorOutput);
20368
- directRefundTx.addOutput({
20369
- script: receiverScript,
20370
- amount: maybeApplyFee(amountSats)
20371
- });
20372
- directFromCpfpRefundTx = new Transaction4();
20373
- directFromCpfpRefundTx.addInput({
20374
- txid: cpfpNodeOutPoint.txid,
20375
- index: cpfpNodeOutPoint.index,
20376
- sequence: directSequence
20377
- });
20378
- directFromCpfpRefundTx.addInput(connectorOutput);
20379
- directFromCpfpRefundTx.addOutput({
20380
- script: receiverScript,
20381
- amount: maybeApplyFee(amountSats)
20382
- });
20383
- }
20384
- return {
20385
- cpfpRefundTx,
20386
- directRefundTx,
20387
- directFromCpfpRefundTx
20388
- };
20389
- }
20390
20371
  async signCoopExitRefunds(leaves, exitTxId, connectorOutputs, receiverPubKey, transferId) {
20391
20372
  if (leaves.length !== connectorOutputs.length) {
20392
20373
  throw new ValidationError(
@@ -20420,29 +20401,39 @@ var CoopExitService = class extends BaseTransferService {
20420
20401
  expected: "Valid connector output"
20421
20402
  });
20422
20403
  }
20404
+ const nodeTx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
20405
+ let directNodeTx;
20406
+ if (leaf.leaf.directTx.length > 0) {
20407
+ directNodeTx = getTxFromRawTxBytes(leaf.leaf.directTx);
20408
+ }
20423
20409
  const currentRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
20424
- const sequence = currentRefundTx.getInput(0).sequence;
20425
- if (!sequence) {
20410
+ if (!currentRefundTx) {
20411
+ throw new ValidationError("Invalid refund transaction", {
20412
+ field: "currentRefundTx",
20413
+ value: currentRefundTx,
20414
+ expected: "Non-null refund transaction"
20415
+ });
20416
+ }
20417
+ const currentSequence = currentRefundTx.getInput(0).sequence;
20418
+ if (!currentSequence) {
20426
20419
  throw new ValidationError("Invalid refund transaction", {
20427
20420
  field: "sequence",
20428
20421
  value: currentRefundTx.getInput(0),
20429
20422
  expected: "Non-null sequence"
20430
20423
  });
20431
20424
  }
20432
- const { nextSequence, nextDirectSequence } = getNextTransactionSequence(sequence);
20433
20425
  let currentDirectRefundTx;
20434
20426
  if (leaf.leaf.directRefundTx.length > 0) {
20435
20427
  currentDirectRefundTx = getTxFromRawTxBytes(leaf.leaf.directRefundTx);
20436
20428
  }
20437
- const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = this.createConnectorRefundTransactions(
20438
- nextSequence,
20439
- nextDirectSequence,
20440
- currentRefundTx.getInput(0),
20441
- currentDirectRefundTx?.getInput(0),
20429
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createConnectorRefundTxs({
20430
+ nodeTx,
20431
+ directNodeTx,
20432
+ sequence: currentSequence,
20442
20433
  connectorOutput,
20443
- BigInt(leaf.leaf.value),
20444
- receiverPubKey
20445
- );
20434
+ receivingPubkey: receiverPubKey,
20435
+ network: this.config.getNetwork()
20436
+ });
20446
20437
  const signingNonceCommitment = await this.config.signer.getRandomSigningCommitment();
20447
20438
  const directSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
20448
20439
  const directFromCpfpSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
@@ -20834,38 +20825,18 @@ var DepositService = class {
20834
20825
  expected: "Valid output index"
20835
20826
  });
20836
20827
  }
20837
- const script = output.script;
20838
- const amount = output.amount;
20839
- if (!script || !amount) {
20840
- throw new ValidationError("No script or amount found in deposit tx", {
20841
- field: "output",
20842
- value: output,
20843
- expected: "Output with script and amount"
20844
- });
20845
- }
20846
- const depositOutPoint = {
20847
- txid: hexToBytes4(getTxId(depositTx)),
20848
- index: vout
20849
- };
20850
- const depositTxOut = {
20851
- script,
20852
- amount
20853
- };
20854
- const [cpfpRootTx, directRootTx] = createRootTx(
20855
- depositOutPoint,
20856
- depositTxOut
20828
+ const { nodeTx: cpfpRootTx, directNodeTx: directRootTx } = createRootNodeTx(
20829
+ depositTx,
20830
+ vout
20857
20831
  );
20858
20832
  const cpfpRootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
20859
20833
  const directRootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
20860
20834
  const cpfpRootTxSighash = getSigHashFromTx(cpfpRootTx, 0, output);
20861
20835
  const directRootTxSighash = getSigHashFromTx(directRootTx, 0, output);
20862
20836
  const signingPubKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
20863
- const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
20864
- sequence: INITIAL_SEQUENCE,
20865
- directSequence: INITIAL_DIRECT_SEQUENCE,
20866
- input: { txid: hexToBytes4(getTxId(cpfpRootTx)), index: 0 },
20867
- directInput: { txid: hexToBytes4(getTxId(directRootTx)), index: 0 },
20868
- amountSats: amount,
20837
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createInitialTimelockRefundTxs({
20838
+ nodeTx: cpfpRootTx,
20839
+ directNodeTx: directRootTx,
20869
20840
  receivingPubkey: signingPubKey,
20870
20841
  network: this.config.getNetwork()
20871
20842
  });
@@ -21157,22 +21128,12 @@ var DepositService = class {
21157
21128
  expected: "Output with script and amount"
21158
21129
  });
21159
21130
  }
21160
- const depositOutPoint = {
21161
- txid: hexToBytes4(getTxId(depositTx)),
21162
- index: vout
21163
- };
21164
- const depositTxOut = {
21165
- script,
21166
- amount
21167
- };
21168
- const [cpfpRootTx, _] = createRootTx(depositOutPoint, depositTxOut);
21131
+ const { nodeTx: cpfpRootTx } = createRootNodeTx(depositTx, vout);
21169
21132
  const cpfpRootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
21170
21133
  const cpfpRootTxSighash = getSigHashFromTx(cpfpRootTx, 0, output);
21171
21134
  const signingPubKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
21172
- const { cpfpRefundTx } = createRefundTxs({
21173
- sequence: INITIAL_SEQUENCE,
21174
- input: { txid: hexToBytes4(getTxId(cpfpRootTx)), index: 0 },
21175
- amountSats: amount,
21135
+ const { cpfpRefundTx } = createInitialTimelockRefundTxs({
21136
+ nodeTx: cpfpRootTx,
21176
21137
  receivingPubkey: signingPubKey,
21177
21138
  network: this.config.getNetwork()
21178
21139
  });
@@ -27186,7 +27147,7 @@ var isWebExtension = (
27186
27147
  "chrome" in globalThis && globalThis.chrome.runtime?.id
27187
27148
  );
27188
27149
  var userAgent = "navigator" in globalThis ? globalThis.navigator.userAgent || "unknown-user-agent" : void 0;
27189
- var packageVersion = true ? "0.3.8" : "unknown";
27150
+ var packageVersion = true ? "0.4.0" : "unknown";
27190
27151
  var baseEnvStr = "unknown";
27191
27152
  if (isBun) {
27192
27153
  const bunVersion = "version" in globalThis.Bun ? globalThis.Bun.version : "unknown-version";
@@ -27207,12 +27168,9 @@ if (isBun) {
27207
27168
  }
27208
27169
  var clientEnv = `js-spark-sdk/${packageVersion} ${baseEnvStr}`;
27209
27170
 
27210
- // src/services/signing.ts
27211
- import { hexToBytes as hexToBytes9 } from "@noble/curves/utils";
27212
-
27213
27171
  // src/utils/htlc-transactions.ts
27214
27172
  import {
27215
- Transaction as Transaction6,
27173
+ Transaction as Transaction4,
27216
27174
  Script,
27217
27175
  taprootListToTree,
27218
27176
  p2tr as p2tr3,
@@ -27300,7 +27258,7 @@ function createLightningHTLCTransaction({
27300
27258
  txid: hexToBytes8(getTxId(nodeTx)),
27301
27259
  index: 0
27302
27260
  };
27303
- const htlcTransaction = new Transaction6({
27261
+ const htlcTransaction = new Transaction4({
27304
27262
  version: 3,
27305
27263
  allowUnknownOutputs: true
27306
27264
  });
@@ -27419,20 +27377,7 @@ var SigningService = class {
27419
27377
  });
27420
27378
  }
27421
27379
  const nodeTx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
27422
- const cpfpNodeOutPoint = {
27423
- txid: hexToBytes9(getTxId(nodeTx)),
27424
- index: 0
27425
- };
27426
27380
  const currRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
27427
- const sequence = currRefundTx.getInput(0).sequence;
27428
- if (!sequence) {
27429
- throw new ValidationError("Invalid refund transaction", {
27430
- field: "sequence",
27431
- value: currRefundTx.getInput(0),
27432
- expected: "Non-null sequence"
27433
- });
27434
- }
27435
- const { nextSequence, nextDirectSequence } = getNextTransactionSequence(sequence);
27436
27381
  const amountSats = currRefundTx.getOutput(0).amount;
27437
27382
  if (amountSats === void 0) {
27438
27383
  throw new ValidationError("Invalid refund transaction", {
@@ -27442,20 +27387,21 @@ var SigningService = class {
27442
27387
  });
27443
27388
  }
27444
27389
  let directNodeTx;
27445
- let directNodeOutPoint;
27446
27390
  if (leaf.leaf.directTx.length > 0) {
27447
27391
  directNodeTx = getTxFromRawTxBytes(leaf.leaf.directTx);
27448
- directNodeOutPoint = {
27449
- txid: hexToBytes9(getTxId(directNodeTx)),
27450
- index: 0
27451
- };
27452
27392
  }
27453
- const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
27454
- sequence: nextSequence,
27455
- directSequence: nextDirectSequence,
27456
- input: cpfpNodeOutPoint,
27457
- directInput: directNodeOutPoint,
27458
- amountSats,
27393
+ const currentSequence = currRefundTx.getInput(0).sequence;
27394
+ if (!currentSequence) {
27395
+ throw new ValidationError("Invalid refund transaction", {
27396
+ field: "sequence",
27397
+ value: currRefundTx.getInput(0),
27398
+ expected: "Non-null sequence"
27399
+ });
27400
+ }
27401
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createDecrementedTimelockRefundTxs({
27402
+ nodeTx,
27403
+ directNodeTx,
27404
+ sequence: currentSequence,
27459
27405
  receivingPubkey: receiverIdentityPubkey,
27460
27406
  network: this.config.getNetwork()
27461
27407
  });
@@ -27471,7 +27417,8 @@ var SigningService = class {
27471
27417
  cpfpSigningCommitments[i]?.signingNonceCommitments
27472
27418
  );
27473
27419
  cpfpLeafSigningJobs.push(...signingJobs);
27474
- if (directRefundTx) {
27420
+ const isZeroNode = getCurrentTimelock(nodeTx.getInput(0).sequence);
27421
+ if (directRefundTx && !isZeroNode) {
27475
27422
  if (!directNodeTx) {
27476
27423
  throw new ValidationError(
27477
27424
  "Direct node transaction undefined while direct refund transaction is defined",
@@ -27496,16 +27443,6 @@ var SigningService = class {
27496
27443
  directLeafSigningJobs.push(...signingJobs2);
27497
27444
  }
27498
27445
  if (directFromCpfpRefundTx) {
27499
- if (!directNodeTx) {
27500
- throw new ValidationError(
27501
- "Direct node transaction undefined while direct from CPFP refund transaction is defined",
27502
- {
27503
- field: "directNodeTx",
27504
- value: directNodeTx,
27505
- expected: "Non-null direct node transaction"
27506
- }
27507
- );
27508
- }
27509
27446
  const refundSighash2 = getSigHashFromTx(
27510
27447
  directFromCpfpRefundTx,
27511
27448
  0,
@@ -27632,6 +27569,35 @@ var SigningService = class {
27632
27569
  directFromCpfpLeafSigningJobs
27633
27570
  };
27634
27571
  }
27572
+ async signSigningJobs(signingJobs) {
27573
+ const userSignedTxSigningJobs = /* @__PURE__ */ new Map();
27574
+ for (const signingJob of signingJobs) {
27575
+ const rawTx = getTxFromRawTxBytes(signingJob.rawTx);
27576
+ const txOut = signingJob.parentTxOut;
27577
+ const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
27578
+ const userSignature = await this.config.signer.signFrost({
27579
+ message: rawTxSighash,
27580
+ keyDerivation: signingJob.keyDerivation,
27581
+ publicKey: signingJob.signingPublicKey,
27582
+ verifyingKey: signingJob.verifyingKey,
27583
+ selfCommitment: signingJob.signingNonceCommitment,
27584
+ statechainCommitments: signingJob.signingNonceCommitments,
27585
+ adaptorPubKey: new Uint8Array()
27586
+ });
27587
+ const userSignedTxSigningJob = {
27588
+ leafId: signingJob.leafId,
27589
+ signingPublicKey: signingJob.signingPublicKey,
27590
+ rawTx: rawTx.toBytes(),
27591
+ signingNonceCommitment: signingJob.signingNonceCommitment.commitment,
27592
+ signingCommitments: {
27593
+ signingCommitments: signingJob.signingNonceCommitments
27594
+ },
27595
+ userSignature
27596
+ };
27597
+ userSignedTxSigningJobs.set(signingJob.type, userSignedTxSigningJob);
27598
+ }
27599
+ return userSignedTxSigningJobs;
27600
+ }
27635
27601
  };
27636
27602
 
27637
27603
  // src/signer/signer.ts
@@ -27641,7 +27607,7 @@ import {
27641
27607
  bytesToHex as bytesToHex8,
27642
27608
  bytesToNumberBE as bytesToNumberBE7,
27643
27609
  equalBytes as equalBytes5,
27644
- hexToBytes as hexToBytes10
27610
+ hexToBytes as hexToBytes9
27645
27611
  } from "@noble/curves/utils";
27646
27612
  import { sha256 as sha25610 } from "@noble/hashes/sha2";
27647
27613
  import { HDKey } from "@scure/bip32";
@@ -28295,7 +28261,7 @@ var DefaultSparkSigner = class {
28295
28261
  }
28296
28262
  async createSparkWalletFromSeed(seed, accountNumber) {
28297
28263
  if (typeof seed === "string") {
28298
- seed = hexToBytes10(seed);
28264
+ seed = hexToBytes9(seed);
28299
28265
  }
28300
28266
  const {
28301
28267
  identityKey,
@@ -28434,14 +28400,14 @@ var TaprootSparkSigner = class extends DefaultSparkSigner {
28434
28400
 
28435
28401
  // src/tests/utils/test-faucet.ts
28436
28402
  import { schnorr as schnorr6, secp256k1 as secp256k112 } from "@noble/curves/secp256k1";
28437
- import { bytesToHex as bytesToHex9, hexToBytes as hexToBytes11 } from "@noble/curves/utils";
28403
+ import { bytesToHex as bytesToHex9, hexToBytes as hexToBytes10 } from "@noble/curves/utils";
28438
28404
  import * as btc3 from "@scure/btc-signer";
28439
- import { Address as Address3, OutScript as OutScript2, SigHash as SigHash2, Transaction as Transaction7 } from "@scure/btc-signer";
28405
+ import { Address as Address3, OutScript as OutScript2, SigHash as SigHash2, Transaction as Transaction5 } from "@scure/btc-signer";
28440
28406
  import { taprootTweakPrivKey as taprootTweakPrivKey2 } from "@scure/btc-signer/utils";
28441
- var STATIC_FAUCET_KEY = hexToBytes11(
28407
+ var STATIC_FAUCET_KEY = hexToBytes10(
28442
28408
  "deadbeef1337cafe4242424242424242deadbeef1337cafe4242424242424242"
28443
28409
  );
28444
- var STATIC_MINING_KEY = hexToBytes11(
28410
+ var STATIC_MINING_KEY = hexToBytes10(
28445
28411
  "1337cafe4242deadbeef4242424242421337cafe4242deadbeef424242424242"
28446
28412
  );
28447
28413
  var SATS_PER_BTC = 1e8;
@@ -28524,7 +28490,7 @@ var BitcoinFaucet = class _BitcoinFaucet {
28524
28490
  );
28525
28491
  await this.generateToAddress(1, address);
28526
28492
  const fundingTxRaw = await this.getRawTransaction(fundingTxid);
28527
- const fundingTx = Transaction7.fromRaw(hexToBytes11(fundingTxRaw.hex));
28493
+ const fundingTx = Transaction5.fromRaw(hexToBytes10(fundingTxRaw.hex));
28528
28494
  for (let i = 0; i < fundingTx.outputsLength; i++) {
28529
28495
  const output = fundingTx.getOutput(i);
28530
28496
  if (!output.script || !output.amount) continue;
@@ -28555,7 +28521,7 @@ var BitcoinFaucet = class _BitcoinFaucet {
28555
28521
  `Selected UTXO (${selectedUtxoAmountSats} sats) is too small to create even one faucet coin of ${COIN_AMOUNT} sats`
28556
28522
  );
28557
28523
  }
28558
- const splitTx = new Transaction7();
28524
+ const splitTx = new Transaction5();
28559
28525
  splitTx.addInput({
28560
28526
  txid: selectedUtxo.txid,
28561
28527
  index: selectedUtxo.vout
@@ -28590,7 +28556,7 @@ var BitcoinFaucet = class _BitcoinFaucet {
28590
28556
  this.coins.push({
28591
28557
  key: STATIC_FAUCET_KEY,
28592
28558
  outpoint: {
28593
- txid: hexToBytes11(splitTxId),
28559
+ txid: hexToBytes10(splitTxId),
28594
28560
  index: i
28595
28561
  },
28596
28562
  txout: signedSplitTx.getOutput(i)
@@ -28598,7 +28564,7 @@ var BitcoinFaucet = class _BitcoinFaucet {
28598
28564
  }
28599
28565
  }
28600
28566
  async sendFaucetCoinToP2WPKHAddress(pubKey) {
28601
- const sendToPubKeyTx = new Transaction7();
28567
+ const sendToPubKeyTx = new Transaction5();
28602
28568
  const p2wpkhAddress = btc3.p2wpkh(pubKey, getNetwork(4 /* LOCAL */)).address;
28603
28569
  if (!p2wpkhAddress) {
28604
28570
  throw new Error("Invalid P2WPKH address");
@@ -28653,6 +28619,14 @@ var BitcoinFaucet = class _BitcoinFaucet {
28653
28619
  async mineBlocks(numBlocks) {
28654
28620
  return await this.generateToAddress(numBlocks, this.miningAddress);
28655
28621
  }
28622
+ async mineBlocksAndWaitForMiningToComplete(numBlocks) {
28623
+ const startBlock = await this.getBlockCount();
28624
+ await this.mineBlocks(numBlocks);
28625
+ await this.waitForBlocksMined({
28626
+ startBlock,
28627
+ expectedIncrease: numBlocks
28628
+ });
28629
+ }
28656
28630
  async call(method, params) {
28657
28631
  try {
28658
28632
  const { fetch, Headers: Headers3 } = getFetch();
@@ -28705,21 +28679,56 @@ var BitcoinFaucet = class _BitcoinFaucet {
28705
28679
  async getBlock(blockHash) {
28706
28680
  return await this.call("getblock", [blockHash, 2]);
28707
28681
  }
28682
+ async getBlockCount() {
28683
+ return await this.call("getblockcount", []);
28684
+ }
28685
+ async waitForBlocksMined({
28686
+ startBlock,
28687
+ expectedIncrease,
28688
+ timeoutMs = 3e4,
28689
+ intervalMs = 5e3
28690
+ }) {
28691
+ const deadline = Date.now() + timeoutMs;
28692
+ await new Promise((r) => setTimeout(r, intervalMs));
28693
+ const start = startBlock;
28694
+ const target = start + expectedIncrease;
28695
+ while (Date.now() < deadline) {
28696
+ const currentBlock = await this.getBlockCount();
28697
+ if (currentBlock >= target) return currentBlock;
28698
+ await new Promise((r) => setTimeout(r, intervalMs));
28699
+ }
28700
+ throw new Error(
28701
+ `Timed out waiting for ${expectedIncrease} blocks (target height ${target})`
28702
+ );
28703
+ }
28708
28704
  async broadcastTx(txHex) {
28709
28705
  let response = await this.call("sendrawtransaction", [txHex, 0]);
28710
28706
  return response;
28711
28707
  }
28708
+ async submitPackage(txHexs) {
28709
+ let response = await this.call("submitpackage", [txHexs]);
28710
+ return response;
28711
+ }
28712
28712
  async getNewAddress() {
28713
28713
  const key = secp256k112.utils.randomPrivateKey();
28714
28714
  const pubKey = secp256k112.getPublicKey(key);
28715
28715
  return getP2TRAddressFromPublicKey(pubKey, 4 /* LOCAL */);
28716
28716
  }
28717
+ async getNewExternalWallet() {
28718
+ const key = secp256k112.utils.randomPrivateKey();
28719
+ const pubKey = secp256k112.getPublicKey(key);
28720
+ return {
28721
+ address: getP2TRAddressFromPublicKey(pubKey, 4 /* LOCAL */),
28722
+ key,
28723
+ pubKey
28724
+ };
28725
+ }
28717
28726
  async sendToAddress(address, amount, blocksToGenerate = 1) {
28718
28727
  const coin = await this.fund();
28719
28728
  if (!coin) {
28720
28729
  throw new Error("No coins available");
28721
28730
  }
28722
- const tx = new Transaction7();
28731
+ const tx = new Transaction5();
28723
28732
  tx.addInput(coin.outpoint);
28724
28733
  const availableAmount = COIN_AMOUNT - FEE_AMOUNT;
28725
28734
  const destinationAddress = Address3(getNetwork(4 /* LOCAL */)).decode(
@@ -28814,75 +28823,6 @@ function chunkArray(arr, size) {
28814
28823
  return chunks;
28815
28824
  }
28816
28825
 
28817
- // src/utils/optimize.ts
28818
- var DENOMINATIONS = Array.from({ length: 28 }, (_, i) => 2 ** i);
28819
- function assert(condition, message) {
28820
- if (!condition) {
28821
- throw new InternalValidationError(message || "Assertion failed");
28822
- }
28823
- }
28824
- function sum(arr) {
28825
- return arr.reduce((a, b) => a + b, 0);
28826
- }
28827
- function sorted(arr) {
28828
- return [...arr].sort((a, b) => a - b);
28829
- }
28830
- function equals(a, b) {
28831
- return a.length === b.length && a.every((val, index) => val === b[index]);
28832
- }
28833
- function greedyLeaves(amount) {
28834
- const leaves = [];
28835
- let remaining = amount;
28836
- for (let i = DENOMINATIONS.length - 1; i >= 0; i--) {
28837
- const leaf = DENOMINATIONS[i];
28838
- if (typeof leaf === "number" && leaf > 0) {
28839
- while (remaining >= leaf) {
28840
- remaining -= leaf;
28841
- leaves.push(leaf);
28842
- }
28843
- }
28844
- }
28845
- assert(sum(leaves) === amount, "greedy_leaves: sum mismatch");
28846
- return sorted(leaves);
28847
- }
28848
- var Swap = class {
28849
- inLeaves;
28850
- outLeaves;
28851
- constructor(inLeaves, outLeaves) {
28852
- this.inLeaves = [...inLeaves];
28853
- this.outLeaves = [...outLeaves];
28854
- assert(
28855
- sum(this.inLeaves) === sum(this.outLeaves),
28856
- "Swap in/out leaves must sum to same value for swap: " + this.toString()
28857
- );
28858
- }
28859
- toString() {
28860
- return `Swap(in=${JSON.stringify(this.inLeaves)}, out=${JSON.stringify(this.outLeaves)})`;
28861
- }
28862
- };
28863
- function maximizeUnilateralExit(inputLeaves, maxLeavesPerSwap = 64) {
28864
- const swaps = [];
28865
- let batch = [];
28866
- let leaves = sorted(inputLeaves);
28867
- while (leaves.length > 0) {
28868
- batch.push(leaves.shift());
28869
- const target = greedyLeaves(sum(batch));
28870
- if (batch.length >= maxLeavesPerSwap || target.length >= maxLeavesPerSwap) {
28871
- if (!equals(target, batch)) {
28872
- swaps.push(new Swap([...batch], target));
28873
- }
28874
- batch = [];
28875
- }
28876
- }
28877
- if (batch.length > 0) {
28878
- const target = greedyLeaves(sum(batch));
28879
- if (!equals(target, batch)) {
28880
- swaps.push(new Swap([...batch], target));
28881
- }
28882
- }
28883
- return swaps;
28884
- }
28885
-
28886
28826
  // src/utils/retry.ts
28887
28827
  var DEFAULT_RETRY_CONFIG = {
28888
28828
  maxAttempts: 5,
@@ -28959,6 +28899,199 @@ var SparkWalletEvent = {
28959
28899
  StreamReconnecting: "stream:reconnecting"
28960
28900
  };
28961
28901
 
28902
+ // src/utils/optimize.ts
28903
+ var DENOMINATIONS = Array.from({ length: 28 }, (_, i) => 2 ** i);
28904
+ function assert(condition, message) {
28905
+ if (!condition) {
28906
+ throw new InternalValidationError(message || "Assertion failed");
28907
+ }
28908
+ }
28909
+ function sum(arr) {
28910
+ return arr.reduce((a, b) => a + b, 0);
28911
+ }
28912
+ function sorted(arr) {
28913
+ return [...arr].sort((a, b) => a - b);
28914
+ }
28915
+ function equals(a, b) {
28916
+ return a.length === b.length && a.every((val, index) => val === b[index]);
28917
+ }
28918
+ function countOccurrences(arr) {
28919
+ const map = /* @__PURE__ */ new Map();
28920
+ for (const x of arr) {
28921
+ map.set(x, (map.get(x) ?? 0) + 1);
28922
+ }
28923
+ return map;
28924
+ }
28925
+ function subtractCounters(a, b) {
28926
+ const result = /* @__PURE__ */ new Map();
28927
+ for (const [key, value] of a.entries()) {
28928
+ const diff = value - (b.get(key) ?? 0);
28929
+ if (diff > 0) {
28930
+ result.set(key, diff);
28931
+ }
28932
+ }
28933
+ return result;
28934
+ }
28935
+ function counterToFlatArray(counter) {
28936
+ const arr = [];
28937
+ for (const [k, v] of Array.from(counter.entries()).sort(
28938
+ (a, b) => a[0] - b[0]
28939
+ )) {
28940
+ for (let i = 0; i < v; i++) {
28941
+ arr.push(k);
28942
+ }
28943
+ }
28944
+ return arr;
28945
+ }
28946
+ function greedyLeaves(amount) {
28947
+ const leaves = [];
28948
+ let remaining = amount;
28949
+ for (let i = DENOMINATIONS.length - 1; i >= 0; i--) {
28950
+ const leaf = DENOMINATIONS[i];
28951
+ if (typeof leaf === "number" && leaf > 0) {
28952
+ while (remaining >= leaf) {
28953
+ remaining -= leaf;
28954
+ leaves.push(leaf);
28955
+ }
28956
+ }
28957
+ }
28958
+ assert(sum(leaves) === amount, "greedy_leaves: sum mismatch");
28959
+ return sorted(leaves);
28960
+ }
28961
+ function swapMinimizingLeaves(amount, multiplicity = 1) {
28962
+ const leaves = [];
28963
+ let remaining = amount;
28964
+ assert(multiplicity > 0, "multiplicity must be > 0");
28965
+ for (const leaf of DENOMINATIONS) {
28966
+ if (typeof leaf === "number" && leaf > 0) {
28967
+ for (let i = 0; i < multiplicity; i++) {
28968
+ if (remaining >= leaf) {
28969
+ remaining -= leaf;
28970
+ leaves.push(leaf);
28971
+ }
28972
+ }
28973
+ }
28974
+ }
28975
+ leaves.push(...greedyLeaves(remaining));
28976
+ assert(sum(leaves) === amount, "swap_minimizing_leaves: sum mismatch");
28977
+ return sorted(leaves);
28978
+ }
28979
+ var Swap = class {
28980
+ inLeaves;
28981
+ outLeaves;
28982
+ constructor(inLeaves, outLeaves) {
28983
+ this.inLeaves = [...inLeaves];
28984
+ this.outLeaves = [...outLeaves];
28985
+ assert(
28986
+ sum(this.inLeaves) === sum(this.outLeaves),
28987
+ "Swap in/out leaves must sum to same value for swap: " + this.toString()
28988
+ );
28989
+ }
28990
+ toString() {
28991
+ return `Swap(in=${JSON.stringify(this.inLeaves)}, out=${JSON.stringify(this.outLeaves)})`;
28992
+ }
28993
+ };
28994
+ function maximizeUnilateralExit(inputLeaves, maxLeavesPerSwap = 64) {
28995
+ const swaps = [];
28996
+ let batch = [];
28997
+ let leaves = sorted(inputLeaves);
28998
+ while (leaves.length > 0) {
28999
+ batch.push(leaves.shift());
29000
+ const target = greedyLeaves(sum(batch));
29001
+ if (batch.length >= maxLeavesPerSwap || target.length >= maxLeavesPerSwap) {
29002
+ if (!equals(target, batch)) {
29003
+ swaps.push(new Swap([...batch], target));
29004
+ }
29005
+ batch = [];
29006
+ }
29007
+ }
29008
+ if (batch.length > 0) {
29009
+ const target = greedyLeaves(sum(batch));
29010
+ if (!equals(target, batch)) {
29011
+ swaps.push(new Swap([...batch], target));
29012
+ }
29013
+ }
29014
+ return swaps;
29015
+ }
29016
+ function minimizeTransferSwap(inputLeaves, multiplicity = 1, maxLeavesPerSwap = 64) {
29017
+ const balance = sum(inputLeaves);
29018
+ const optimalLeaves = swapMinimizingLeaves(balance, multiplicity);
29019
+ const walletCounter = countOccurrences(inputLeaves);
29020
+ const optimalCounter = countOccurrences(optimalLeaves);
29021
+ const leavesToGive = subtractCounters(walletCounter, optimalCounter);
29022
+ const leavesToReceive = subtractCounters(optimalCounter, walletCounter);
29023
+ const leavesToGiveFlat = counterToFlatArray(leavesToGive);
29024
+ const leavesToReceiveFlat = counterToFlatArray(leavesToReceive);
29025
+ const swaps = [];
29026
+ let toGiveBatch = [];
29027
+ let toReceiveBatch = [];
29028
+ let give = [...leavesToGiveFlat];
29029
+ let receive = [...leavesToReceiveFlat];
29030
+ while (give.length > 0 || receive.length > 0) {
29031
+ if (sum(toGiveBatch) > sum(toReceiveBatch)) {
29032
+ if (receive.length === 0) break;
29033
+ toReceiveBatch.push(receive.shift());
29034
+ } else {
29035
+ if (give.length === 0) break;
29036
+ toGiveBatch.push(give.shift());
29037
+ }
29038
+ if (toGiveBatch.length > 0 && toReceiveBatch.length > 0 && sum(toGiveBatch) === sum(toReceiveBatch)) {
29039
+ if (toGiveBatch.length > maxLeavesPerSwap) {
29040
+ for (let i = 0; i < toGiveBatch.length; i += maxLeavesPerSwap) {
29041
+ const subset = toGiveBatch.slice(i, i + maxLeavesPerSwap);
29042
+ swaps.push(new Swap(subset, greedyLeaves(sum(subset))));
29043
+ }
29044
+ } else if (toReceiveBatch.length > maxLeavesPerSwap) {
29045
+ for (let cutoff = maxLeavesPerSwap; cutoff > 0; cutoff--) {
29046
+ const sumCut = sum(toReceiveBatch.slice(0, cutoff));
29047
+ const remainder = sum(toGiveBatch) - sumCut;
29048
+ const alternateBatch = [
29049
+ ...toReceiveBatch.slice(0, cutoff),
29050
+ ...greedyLeaves(remainder)
29051
+ ];
29052
+ if (alternateBatch.length <= maxLeavesPerSwap) {
29053
+ swaps.push(new Swap([...toGiveBatch], alternateBatch));
29054
+ break;
29055
+ }
29056
+ }
29057
+ } else {
29058
+ swaps.push(new Swap([...toGiveBatch], [...toReceiveBatch]));
29059
+ }
29060
+ toGiveBatch = [];
29061
+ toReceiveBatch = [];
29062
+ }
29063
+ }
29064
+ return swaps;
29065
+ }
29066
+ function shouldOptimize(inputLeaves, multiplicity = 1, maxLeavesPerSwap = 64) {
29067
+ if (multiplicity == 0) {
29068
+ const swaps = maximizeUnilateralExit(inputLeaves, maxLeavesPerSwap);
29069
+ const numInputs = sum(swaps.map((swap) => swap.inLeaves.length));
29070
+ const numOutputs = sum(swaps.map((swap) => swap.outLeaves.length));
29071
+ return numOutputs * 5 < numInputs;
29072
+ } else {
29073
+ const swaps = minimizeTransferSwap(
29074
+ inputLeaves,
29075
+ multiplicity,
29076
+ maxLeavesPerSwap
29077
+ );
29078
+ const inputCounter = countOccurrences(
29079
+ swaps.flatMap((swap) => swap.inLeaves)
29080
+ );
29081
+ const outputCounter = countOccurrences(
29082
+ swaps.flatMap((swap) => swap.outLeaves)
29083
+ );
29084
+ return Math.abs(inputCounter.size - outputCounter.size) > 1;
29085
+ }
29086
+ }
29087
+ function optimize(inputLeaves, multiplicity = 1, maxLeavesPerSwap = 64) {
29088
+ if (multiplicity == 0) {
29089
+ return maximizeUnilateralExit(inputLeaves, maxLeavesPerSwap);
29090
+ } else {
29091
+ return minimizeTransferSwap(inputLeaves, multiplicity, maxLeavesPerSwap);
29092
+ }
29093
+ }
29094
+
28962
29095
  // src/spark-wallet/spark-wallet.ts
28963
29096
  var SparkWallet = class extends EventEmitter {
28964
29097
  config;
@@ -29063,16 +29196,13 @@ var SparkWallet = class extends EventEmitter {
29063
29196
  if (event.transfer.transfer && !equalBytes6(senderIdentityPublicKey, receiverIdentityPublicKey)) {
29064
29197
  await this.claimTransfer({
29065
29198
  transfer: event.transfer.transfer,
29066
- emit: true,
29067
- optimize: true
29199
+ emit: true
29068
29200
  });
29069
29201
  }
29070
29202
  } else if (isDepositStreamEvent(event)) {
29071
29203
  const deposit = event.deposit.deposit;
29072
- const newLeaf = await this.transferService.extendTimelock(deposit);
29073
- await this.transferLeavesToSelf(newLeaf.nodes, {
29074
- type: "leaf" /* LEAF */,
29075
- path: deposit.id
29204
+ await this.withLeaves(async () => {
29205
+ this.leaves.push(deposit);
29076
29206
  });
29077
29207
  this.emit(
29078
29208
  SparkWalletEvent.DepositConfirmed,
@@ -29248,19 +29378,6 @@ var SparkWallet = class extends EventEmitter {
29248
29378
  }
29249
29379
  return availableLeaves.filter(([_, node]) => !leavesToIgnore.has(node.id)).map(([_, node]) => node);
29250
29380
  }
29251
- async checkExtendLeaves(leaves) {
29252
- await this.withLeaves(async () => {
29253
- for (const leaf of leaves) {
29254
- if (!leaf.parentNodeId && leaf.status === "AVAILABLE") {
29255
- const res = await this.transferService.extendTimelock(leaf);
29256
- await this.transferLeavesToSelf(res.nodes, {
29257
- type: "leaf" /* LEAF */,
29258
- path: leaf.id
29259
- });
29260
- }
29261
- }
29262
- });
29263
- }
29264
29381
  verifyKey(pubkey1, pubkey2, verifyingKey) {
29265
29382
  return equalBytes6(addPublicKeys(pubkey1, pubkey2), verifyingKey);
29266
29383
  }
@@ -29370,75 +29487,84 @@ var SparkWallet = class extends EventEmitter {
29370
29487
  }
29371
29488
  return nodes;
29372
29489
  }
29373
- areLeavesInefficient() {
29374
- const totalAmount = this.getInternalBalance();
29375
- if (this.leaves.length <= 1) {
29376
- return false;
29490
+ async *optimizeLeaves(multiplicity = void 0) {
29491
+ const multiplicityValue = multiplicity ?? this.config.getOptimizationOptions().multiplicity ?? 0;
29492
+ if (multiplicityValue < 0) {
29493
+ throw new ValidationError("Multiplicity cannot be negative");
29494
+ } else if (multiplicityValue > 5) {
29495
+ throw new ValidationError("Multiplicity cannot be greater than 5");
29377
29496
  }
29378
- const nextLowerPowerOfTwo = 31 - Math.clz32(totalAmount);
29379
- let remainingAmount = totalAmount;
29380
- let optimalLeavesLength = 0;
29381
- for (let i = nextLowerPowerOfTwo; i >= 0; i--) {
29382
- const denomination = 2 ** i;
29383
- while (remainingAmount >= denomination) {
29384
- remainingAmount -= denomination;
29385
- optimalLeavesLength++;
29386
- }
29387
- }
29388
- return this.leaves.length > optimalLeavesLength * 5;
29389
- }
29390
- async optimizeLeaves() {
29391
- if (this.optimizationInProgress || !this.areLeavesInefficient()) {
29497
+ if (this.optimizationInProgress || !shouldOptimize(
29498
+ this.leaves.map((leaf) => leaf.value),
29499
+ multiplicityValue
29500
+ )) {
29392
29501
  return;
29393
29502
  }
29394
- await this.withLeaves(async () => {
29503
+ const controller = new AbortController();
29504
+ const release = await this.leavesMutex.acquire();
29505
+ try {
29395
29506
  this.optimizationInProgress = true;
29396
- try {
29397
- this.leaves = await this.getLeaves();
29398
- const swaps = maximizeUnilateralExit(
29399
- this.leaves.map((leaf) => leaf.value)
29400
- );
29401
- const valueToNodes = /* @__PURE__ */ new Map();
29402
- this.leaves.forEach((leaf) => {
29403
- if (!valueToNodes.has(leaf.value)) {
29404
- valueToNodes.set(leaf.value, []);
29405
- }
29406
- valueToNodes.get(leaf.value).push(leaf);
29407
- });
29408
- for (const swap of swaps) {
29409
- const leavesToSend = [];
29410
- for (const leafValue of swap.inLeaves) {
29411
- const nodes = valueToNodes.get(leafValue);
29412
- if (nodes && nodes.length > 0) {
29413
- const node = nodes.shift();
29414
- leavesToSend.push(node);
29415
- } else {
29416
- throw new InternalValidationError(
29417
- `No unused leaf with value ${leafValue} found in leaves`
29418
- );
29419
- }
29507
+ this.leaves = await this.getLeaves();
29508
+ const swaps = optimize(
29509
+ this.leaves.map((leaf) => leaf.value),
29510
+ multiplicityValue
29511
+ );
29512
+ if (swaps.length === 0) {
29513
+ return;
29514
+ }
29515
+ yield {
29516
+ step: 0,
29517
+ total: swaps.length,
29518
+ controller
29519
+ };
29520
+ const valueToNodes = /* @__PURE__ */ new Map();
29521
+ this.leaves.forEach((leaf) => {
29522
+ if (!valueToNodes.has(leaf.value)) {
29523
+ valueToNodes.set(leaf.value, []);
29524
+ }
29525
+ valueToNodes.get(leaf.value).push(leaf);
29526
+ });
29527
+ for (const swap of swaps) {
29528
+ if (controller.signal.aborted) {
29529
+ break;
29530
+ }
29531
+ const leavesToSend = [];
29532
+ for (const leafValue of swap.inLeaves) {
29533
+ const nodes = valueToNodes.get(leafValue);
29534
+ if (nodes && nodes.length > 0) {
29535
+ const node = nodes.shift();
29536
+ leavesToSend.push(node);
29537
+ } else {
29538
+ throw new InternalValidationError(
29539
+ `No unused leaf with value ${leafValue} found in leaves`
29540
+ );
29420
29541
  }
29421
- await this.requestLeavesSwap({
29422
- leaves: leavesToSend,
29423
- targetAmounts: swap.outLeaves
29424
- });
29425
29542
  }
29426
- this.leaves = await this.getLeaves();
29427
- } finally {
29428
- this.optimizationInProgress = false;
29543
+ await this.requestLeavesSwap({
29544
+ leaves: leavesToSend,
29545
+ targetAmounts: swap.outLeaves
29546
+ });
29547
+ yield {
29548
+ step: swaps.indexOf(swap) + 1,
29549
+ total: swaps.length,
29550
+ controller
29551
+ };
29429
29552
  }
29430
- });
29553
+ this.leaves = await this.getLeaves();
29554
+ } finally {
29555
+ this.optimizationInProgress = false;
29556
+ release();
29557
+ }
29431
29558
  }
29432
29559
  async syncWallet() {
29433
29560
  await this.syncTokenOutputs();
29434
29561
  let leaves = await this.getLeaves();
29435
- leaves = await this.checkRefreshTimelockNodes(leaves);
29436
- leaves = await this.checkExtendTimeLockNodes(leaves);
29562
+ leaves = await this.checkRenewLeaves(leaves);
29437
29563
  this.leaves = leaves;
29438
- this.checkExtendLeaves(leaves);
29439
- this.optimizeLeaves().catch((e) => {
29440
- console.error("Failed to optimize leaves", e);
29441
- });
29564
+ if (this.config.getOptimizationOptions().auto) {
29565
+ for await (const _ of this.optimizeLeaves()) {
29566
+ }
29567
+ }
29442
29568
  }
29443
29569
  async withLeaves(operation) {
29444
29570
  const release = await this.leavesMutex.acquire();
@@ -29555,7 +29681,7 @@ var SparkWallet = class extends EventEmitter {
29555
29681
  mnemonic = mnemonicOrSeed;
29556
29682
  seed = await this.config.signer.mnemonicToSeed(mnemonicOrSeed);
29557
29683
  } else {
29558
- seed = hexToBytes12(mnemonicOrSeed);
29684
+ seed = hexToBytes11(mnemonicOrSeed);
29559
29685
  }
29560
29686
  }
29561
29687
  await this.initWalletFromSeed(seed, accountNumber);
@@ -29698,7 +29824,7 @@ var SparkWallet = class extends EventEmitter {
29698
29824
  directFromCpfpSignatureMap
29699
29825
  } = await this.transferService.startSwapSignRefund(
29700
29826
  leafKeyTweaks,
29701
- hexToBytes12(this.config.getSspIdentityPublicKey()),
29827
+ hexToBytes11(this.config.getSspIdentityPublicKey()),
29702
29828
  new Date(Date.now() + 2 * 60 * 1e3)
29703
29829
  );
29704
29830
  try {
@@ -29913,7 +30039,7 @@ var SparkWallet = class extends EventEmitter {
29913
30039
  throw new Error(`Leaf not found for node ${nodeId}`);
29914
30040
  }
29915
30041
  const cpfpNodeTx = getTxFromRawTxBytes(node.nodeTx);
29916
- const cpfpRefundTxBytes = hexToBytes12(leaf.rawUnsignedRefundTransaction);
30042
+ const cpfpRefundTxBytes = hexToBytes11(leaf.rawUnsignedRefundTransaction);
29917
30043
  const cpfpRefundTx = getTxFromRawTxBytes(cpfpRefundTxBytes);
29918
30044
  const cpfpSighash = getSigHashFromTx(
29919
30045
  cpfpRefundTx,
@@ -29922,7 +30048,7 @@ var SparkWallet = class extends EventEmitter {
29922
30048
  );
29923
30049
  const nodePublicKey = node.verifyingPublicKey;
29924
30050
  const taprootKey = computeTaprootKeyNoScript(nodePublicKey.slice(1));
29925
- const cpfpAdaptorSignatureBytes = hexToBytes12(
30051
+ const cpfpAdaptorSignatureBytes = hexToBytes11(
29926
30052
  leaf.adaptorSignedSignature
29927
30053
  );
29928
30054
  applyAdaptorToSignature(
@@ -29933,7 +30059,7 @@ var SparkWallet = class extends EventEmitter {
29933
30059
  );
29934
30060
  if (leaf.directRawUnsignedRefundTransaction) {
29935
30061
  const directNodeTx = getTxFromRawTxBytes(node.directTx);
29936
- const directRefundTxBytes = hexToBytes12(
30062
+ const directRefundTxBytes = hexToBytes11(
29937
30063
  leaf.directRawUnsignedRefundTransaction
29938
30064
  );
29939
30065
  const directRefundTx = getTxFromRawTxBytes(directRefundTxBytes);
@@ -29947,7 +30073,7 @@ var SparkWallet = class extends EventEmitter {
29947
30073
  `Direct adaptor signed signature missing for node ${nodeId}`
29948
30074
  );
29949
30075
  }
29950
- const directAdaptorSignatureBytes = hexToBytes12(
30076
+ const directAdaptorSignatureBytes = hexToBytes11(
29951
30077
  leaf.directAdaptorSignedSignature
29952
30078
  );
29953
30079
  applyAdaptorToSignature(
@@ -29958,7 +30084,7 @@ var SparkWallet = class extends EventEmitter {
29958
30084
  );
29959
30085
  }
29960
30086
  if (leaf.directFromCpfpRawUnsignedRefundTransaction) {
29961
- const directFromCpfpRefundTxBytes = hexToBytes12(
30087
+ const directFromCpfpRefundTxBytes = hexToBytes11(
29962
30088
  leaf.directFromCpfpRawUnsignedRefundTransaction
29963
30089
  );
29964
30090
  const directFromCpfpRefundTx = getTxFromRawTxBytes(
@@ -29974,7 +30100,7 @@ var SparkWallet = class extends EventEmitter {
29974
30100
  `Direct adaptor signed signature missing for node ${nodeId}`
29975
30101
  );
29976
30102
  }
29977
- const directFromCpfpAdaptorSignatureBytes = hexToBytes12(
30103
+ const directFromCpfpAdaptorSignatureBytes = hexToBytes11(
29978
30104
  leaf.directFromCpfpAdaptorSignedSignature
29979
30105
  );
29980
30106
  applyAdaptorToSignature(
@@ -30021,8 +30147,7 @@ var SparkWallet = class extends EventEmitter {
30021
30147
  }
30022
30148
  return await this.claimTransfer({
30023
30149
  transfer: incomingTransfer,
30024
- emit: false,
30025
- optimize: false
30150
+ emit: false
30026
30151
  });
30027
30152
  } catch (e) {
30028
30153
  console.error("[processSwapBatch] Error details:", {
@@ -30416,7 +30541,7 @@ var SparkWallet = class extends EventEmitter {
30416
30541
  }
30417
30542
  );
30418
30543
  }
30419
- const tx = new Transaction8();
30544
+ const tx = new Transaction6();
30420
30545
  tx.addInput({
30421
30546
  txid: depositTransactionId,
30422
30547
  index: outputIndex,
@@ -30456,7 +30581,7 @@ var SparkWallet = class extends EventEmitter {
30456
30581
  );
30457
30582
  const swapResponse = await sparkClient.initiate_static_deposit_utxo_refund({
30458
30583
  onChainUtxo: {
30459
- txid: hexToBytes12(depositTransactionId),
30584
+ txid: hexToBytes11(depositTransactionId),
30460
30585
  vout: outputIndex,
30461
30586
  network: networkType
30462
30587
  },
@@ -30583,7 +30708,7 @@ var SparkWallet = class extends EventEmitter {
30583
30708
  creditAmountView.setUint32(0, lowerHalf, true);
30584
30709
  creditAmountView.setUint32(4, upperHalf, true);
30585
30710
  parts.push(new Uint8Array(creditAmountBuffer));
30586
- parts.push(hexToBytes12(sspSignature));
30711
+ parts.push(hexToBytes11(sspSignature));
30587
30712
  const totalLength = parts.reduce((sum2, part) => sum2 + part.length, 0);
30588
30713
  const payload = new Uint8Array(totalLength);
30589
30714
  let offset = 0;
@@ -30687,26 +30812,7 @@ var SparkWallet = class extends EventEmitter {
30687
30812
  depositTx,
30688
30813
  vout
30689
30814
  });
30690
- const resultingNodes = [];
30691
- for (const node of res.nodes) {
30692
- if (node.status === "AVAILABLE") {
30693
- const { nodes } = await this.transferService.extendTimelock(node);
30694
- for (const n of nodes) {
30695
- if (n.status === "AVAILABLE") {
30696
- const transfer = await this.transferLeavesToSelf([n], {
30697
- type: "leaf" /* LEAF */,
30698
- path: node.id
30699
- });
30700
- resultingNodes.push(...transfer);
30701
- } else {
30702
- resultingNodes.push(n);
30703
- }
30704
- }
30705
- } else {
30706
- resultingNodes.push(node);
30707
- }
30708
- }
30709
- return resultingNodes;
30815
+ return res.nodes;
30710
30816
  }
30711
30817
  /**
30712
30818
  * Gets all unused deposit addresses for the wallet.
@@ -30848,6 +30954,9 @@ var SparkWallet = class extends EventEmitter {
30848
30954
  depositTx,
30849
30955
  vout
30850
30956
  });
30957
+ await this.withLeaves(async () => {
30958
+ this.leaves.push(...nodes2);
30959
+ });
30851
30960
  return nodes2;
30852
30961
  });
30853
30962
  this.mutexes.delete(txid);
@@ -30977,7 +31086,7 @@ var SparkWallet = class extends EventEmitter {
30977
31086
  const [outcome] = await this.transferWithInvoice([
30978
31087
  {
30979
31088
  amountSats,
30980
- receiverIdentityPubkey: hexToBytes12(receiverAddress.identityPublicKey)
31089
+ receiverIdentityPubkey: hexToBytes11(receiverAddress.identityPublicKey)
30981
31090
  }
30982
31091
  ]);
30983
31092
  if (!outcome) throw new Error("no transfer created");
@@ -31024,8 +31133,7 @@ var SparkWallet = class extends EventEmitter {
31024
31133
  `TreeNode group at index ${groupIndex} not found for amount ${amount} after selection`
31025
31134
  );
31026
31135
  }
31027
- let available = await this.checkRefreshTimelockNodes(group);
31028
- available = await this.checkExtendTimeLockNodes(available);
31136
+ const available = await this.checkRenewLeaves(group);
31029
31137
  if (available.length < group.length) {
31030
31138
  throw new Error(
31031
31139
  `Not enough available nodes after refresh/extend. Expected ${group.length}, got ${available.length}`
@@ -31068,7 +31176,7 @@ var SparkWallet = class extends EventEmitter {
31068
31176
  transfer.id
31069
31177
  );
31070
31178
  if (pending) {
31071
- await this.claimTransfer({ transfer: pending, optimize: true });
31179
+ await this.claimTransfer({ transfer: pending });
31072
31180
  }
31073
31181
  }
31074
31182
  return {
@@ -31118,75 +31226,45 @@ var SparkWallet = class extends EventEmitter {
31118
31226
  newKeyDerivation: { type: "random" /* RANDOM */ }
31119
31227
  };
31120
31228
  }
31121
- async checkExtendTimeLockNodes(nodes) {
31122
- const nodesToExtend = [];
31229
+ async checkRenewLeaves(nodes) {
31230
+ const nodesToRenewNode = [];
31231
+ const nodesToRenewRefund = [];
31232
+ const nodesToRenewZeroTimelock = [];
31123
31233
  const nodeIds = [];
31124
31234
  const validNodes = [];
31125
31235
  for (const node of nodes) {
31126
31236
  const nodeTx = getTxFromRawTxBytes(node.nodeTx);
31127
- const sequence = nodeTx.getInput(0).sequence;
31128
- if (!sequence) {
31237
+ const refundTx = getTxFromRawTxBytes(node.refundTx);
31238
+ const nodeSequence = nodeTx.getInput(0).sequence;
31239
+ const refundSequence = refundTx.getInput(0).sequence;
31240
+ if (nodeSequence === void 0) {
31129
31241
  throw new ValidationError("Invalid node transaction", {
31130
31242
  field: "sequence",
31131
31243
  value: nodeTx.getInput(0),
31132
31244
  expected: "Non-null sequence"
31133
31245
  });
31134
31246
  }
31135
- const needsRefresh = doesLeafNeedRefresh(sequence, true);
31136
- if (needsRefresh) {
31137
- nodesToExtend.push(node);
31138
- nodeIds.push(node.id);
31139
- } else {
31140
- validNodes.push(node);
31141
- }
31142
- }
31143
- if (nodesToExtend.length === 0) {
31144
- return validNodes;
31145
- }
31146
- const nodesToAdd = [];
31147
- for (const node of nodesToExtend) {
31148
- const { nodes: nodes2 } = await this.transferService.extendTimelock(node);
31149
- this.leaves = this.leaves.filter((leaf) => leaf.id !== node.id);
31150
- const newNodes = await this.transferLeavesToSelf(nodes2, {
31151
- type: "leaf" /* LEAF */,
31152
- path: node.id
31153
- });
31154
- nodesToAdd.push(...newNodes);
31155
- }
31156
- this.updateLeaves(nodeIds, nodesToAdd);
31157
- validNodes.push(...nodesToAdd);
31158
- return validNodes;
31159
- }
31160
- /**
31161
- * Internal method to refresh timelock nodes.
31162
- *
31163
- * @param {string} nodeId - The optional ID of the node to refresh. If not provided, all nodes will be checked.
31164
- * @returns {Promise<void>}
31165
- * @private
31166
- */
31167
- async checkRefreshTimelockNodes(nodes) {
31168
- const nodesToRefresh = [];
31169
- const nodeIds = [];
31170
- const validNodes = [];
31171
- for (const node of nodes) {
31172
- const refundTx = getTxFromRawTxBytes(node.refundTx);
31173
- const sequence = refundTx.getInput(0).sequence;
31174
- if (!sequence) {
31247
+ if (!refundSequence) {
31175
31248
  throw new ValidationError("Invalid refund transaction", {
31176
31249
  field: "sequence",
31177
31250
  value: refundTx.getInput(0),
31178
31251
  expected: "Non-null sequence"
31179
31252
  });
31180
31253
  }
31181
- const needsRefresh = doesLeafNeedRefresh(sequence);
31182
- if (needsRefresh) {
31183
- nodesToRefresh.push(node);
31254
+ if (doesTxnNeedRenewed(refundSequence)) {
31255
+ if (isZeroTimelock(nodeSequence)) {
31256
+ nodesToRenewZeroTimelock.push(node);
31257
+ } else if (doesTxnNeedRenewed(nodeSequence)) {
31258
+ nodesToRenewNode.push(node);
31259
+ } else {
31260
+ nodesToRenewRefund.push(node);
31261
+ }
31184
31262
  nodeIds.push(node.id);
31185
31263
  } else {
31186
31264
  validNodes.push(node);
31187
31265
  }
31188
31266
  }
31189
- if (nodesToRefresh.length === 0) {
31267
+ if (nodesToRenewNode.length === 0 && nodesToRenewRefund.length === 0 && nodesToRenewZeroTimelock.length === 0) {
31190
31268
  return validNodes;
31191
31269
  }
31192
31270
  const nodesResp = await this.queryNodes({
@@ -31204,7 +31282,18 @@ var SparkWallet = class extends EventEmitter {
31204
31282
  nodesMap.set(node.id, node);
31205
31283
  }
31206
31284
  const nodesToAdd = [];
31207
- for (const node of nodesToRefresh) {
31285
+ for (const node of nodesToRenewNode) {
31286
+ if (!node.parentNodeId) {
31287
+ throw new Error(`node ${node.id} has no parent`);
31288
+ }
31289
+ const parentNode = nodesMap.get(node.parentNodeId);
31290
+ if (!parentNode) {
31291
+ throw new Error(`parent node ${node.parentNodeId} not found`);
31292
+ }
31293
+ const newNode = await this.transferService.renewNodeTxn(node, parentNode);
31294
+ nodesToAdd.push(newNode);
31295
+ }
31296
+ for (const node of nodesToRenewRefund) {
31208
31297
  if (!node.parentNodeId) {
31209
31298
  throw new Error(`node ${node.id} has no parent`);
31210
31299
  }
@@ -31212,17 +31301,14 @@ var SparkWallet = class extends EventEmitter {
31212
31301
  if (!parentNode) {
31213
31302
  throw new Error(`parent node ${node.parentNodeId} not found`);
31214
31303
  }
31215
- const { nodes: nodes2 } = await this.transferService.refreshTimelockNodes(
31304
+ const newNode = await this.transferService.renewRefundTxn(
31216
31305
  node,
31217
31306
  parentNode
31218
31307
  );
31219
- if (nodes2.length !== 1) {
31220
- throw new Error(`expected 1 node, got ${nodes2.length}`);
31221
- }
31222
- const newNode = nodes2[0];
31223
- if (!newNode) {
31224
- throw new Error("Failed to refresh timelock node");
31225
- }
31308
+ nodesToAdd.push(newNode);
31309
+ }
31310
+ for (const node of nodesToRenewZeroTimelock) {
31311
+ const newNode = await this.transferService.renewZeroTimelockNodeTxn(node);
31226
31312
  nodesToAdd.push(newNode);
31227
31313
  }
31228
31314
  this.updateLeaves(nodeIds, nodesToAdd);
@@ -31263,14 +31349,14 @@ var SparkWallet = class extends EventEmitter {
31263
31349
  return response.nodes;
31264
31350
  });
31265
31351
  }
31266
- async processClaimedTransferResults(result, transfer, emit, optimize) {
31267
- result = await this.checkRefreshTimelockNodes(result);
31268
- result = await this.checkExtendTimeLockNodes(result);
31352
+ async processClaimedTransferResults(result, transfer, emit) {
31353
+ result = await this.checkRenewLeaves(result);
31269
31354
  const existingIds = new Set(this.leaves.map((leaf) => leaf.id));
31270
31355
  const uniqueResults = result.filter((node) => !existingIds.has(node.id));
31271
31356
  this.leaves.push(...uniqueResults);
31272
- if (optimize && transfer.type !== 40 /* COUNTER_SWAP */) {
31273
- await this.optimizeLeaves();
31357
+ if (this.config.getOptimizationOptions().auto && transfer.type !== 40 /* COUNTER_SWAP */) {
31358
+ for await (const _ of this.optimizeLeaves()) {
31359
+ }
31274
31360
  }
31275
31361
  if (emit) {
31276
31362
  this.emit(
@@ -31289,8 +31375,7 @@ var SparkWallet = class extends EventEmitter {
31289
31375
  */
31290
31376
  async claimTransfer({
31291
31377
  transfer,
31292
- emit,
31293
- optimize
31378
+ emit
31294
31379
  }) {
31295
31380
  const onError = async (context) => {
31296
31381
  const error = context.error;
@@ -31335,12 +31420,7 @@ var SparkWallet = class extends EventEmitter {
31335
31420
  if (result.length === 0) {
31336
31421
  return [];
31337
31422
  }
31338
- return await this.processClaimedTransferResults(
31339
- result,
31340
- transfer,
31341
- emit,
31342
- optimize
31343
- );
31423
+ return await this.processClaimedTransferResults(result, transfer, emit);
31344
31424
  } catch (error) {
31345
31425
  console.warn(
31346
31426
  `Failed to claim transfer after all retries. Please try reinitializing your wallet in a few minutes. Transfer ID: ${transfer.id}`,
@@ -31373,7 +31453,7 @@ var SparkWallet = class extends EventEmitter {
31373
31453
  continue;
31374
31454
  }
31375
31455
  promises.push(
31376
- this.claimTransfer({ transfer, emit, optimize: true }).then(() => transfer.id).catch((error) => {
31456
+ this.claimTransfer({ transfer, emit }).then(() => transfer.id).catch((error) => {
31377
31457
  console.warn(`Failed to claim transfer ${transfer.id}:`, error);
31378
31458
  return null;
31379
31459
  })
@@ -31599,7 +31679,7 @@ var SparkWallet = class extends EventEmitter {
31599
31679
  const sparkFallbackAddress = decodedInvoice.fallbackAddress;
31600
31680
  const paymentHash = decodedInvoice.paymentHash;
31601
31681
  if (preferSpark) {
31602
- if (sparkFallbackAddress === void 0 || isValidSparkFallback(hexToBytes12(sparkFallbackAddress)) === false) {
31682
+ if (sparkFallbackAddress === void 0 || isValidSparkFallback(hexToBytes11(sparkFallbackAddress)) === false) {
31603
31683
  console.warn(
31604
31684
  "No valid spark address found in invoice. Defaulting to lightning."
31605
31685
  );
@@ -31644,8 +31724,7 @@ var SparkWallet = class extends EventEmitter {
31644
31724
  selectedLeaves,
31645
31725
  `no leaves for ${totalAmount}`
31646
31726
  );
31647
- leaves = await this.checkRefreshTimelockNodes(leaves);
31648
- leaves = await this.checkExtendTimeLockNodes(leaves);
31727
+ leaves = await this.checkRenewLeaves(leaves);
31649
31728
  const leavesToSend = await Promise.all(
31650
31729
  leaves.map(async (leaf) => ({
31651
31730
  leaf,
@@ -31661,17 +31740,17 @@ var SparkWallet = class extends EventEmitter {
31661
31740
  const transferID = uuidv74();
31662
31741
  const startTransferRequest = await this.transferService.prepareTransferForLightning(
31663
31742
  leavesToSend,
31664
- hexToBytes12(this.config.getSspIdentityPublicKey()),
31665
- hexToBytes12(paymentHash),
31743
+ hexToBytes11(this.config.getSspIdentityPublicKey()),
31744
+ hexToBytes11(paymentHash),
31666
31745
  expiryTime,
31667
31746
  transferID
31668
31747
  );
31669
31748
  const swapResponse = await this.lightningService.swapNodesForPreimage({
31670
31749
  leaves: leavesToSend,
31671
- receiverIdentityPubkey: hexToBytes12(
31750
+ receiverIdentityPubkey: hexToBytes11(
31672
31751
  this.config.getSspIdentityPublicKey()
31673
31752
  ),
31674
- paymentHash: hexToBytes12(paymentHash),
31753
+ paymentHash: hexToBytes11(paymentHash),
31675
31754
  isInboundPayment: false,
31676
31755
  invoiceString: invoice,
31677
31756
  feeSats: feeEstimate,
@@ -31774,7 +31853,7 @@ var SparkWallet = class extends EventEmitter {
31774
31853
  }
31775
31854
  satsInvoices.push({
31776
31855
  amountSats: encodedAmount ?? Number(amount),
31777
- receiverIdentityPubkey: hexToBytes12(addressData.identityPublicKey),
31856
+ receiverIdentityPubkey: hexToBytes11(addressData.identityPublicKey),
31778
31857
  sparkInvoice: invoice
31779
31858
  });
31780
31859
  } else if (fields.paymentType?.type === "tokens") {
@@ -31976,10 +32055,8 @@ var SparkWallet = class extends EventEmitter {
31976
32055
  leavesToSendToSsp = leavesForTargetAmount;
31977
32056
  leavesToSendToSE = leavesForFee;
31978
32057
  }
31979
- leavesToSendToSsp = await this.checkRefreshTimelockNodes(leavesToSendToSsp);
31980
- leavesToSendToSsp = await this.checkExtendTimeLockNodes(leavesToSendToSsp);
31981
- leavesToSendToSE = await this.checkRefreshTimelockNodes(leavesToSendToSE);
31982
- leavesToSendToSE = await this.checkExtendTimeLockNodes(leavesToSendToSE);
32058
+ leavesToSendToSsp = await this.checkRenewLeaves(leavesToSendToSsp);
32059
+ leavesToSendToSE = await this.checkRenewLeaves(leavesToSendToSE);
31983
32060
  const leafKeyTweaks = await Promise.all(
31984
32061
  [...leavesToSendToSE, ...leavesToSendToSsp].map(async (leaf) => ({
31985
32062
  leaf,
@@ -32024,11 +32101,11 @@ var SparkWallet = class extends EventEmitter {
32024
32101
  const connectorOutputs = [];
32025
32102
  for (let i = 0; i < connectorTx.outputsLength - 1; i++) {
32026
32103
  connectorOutputs.push({
32027
- txid: hexToBytes12(connectorTxId),
32104
+ txid: hexToBytes11(connectorTxId),
32028
32105
  index: i
32029
32106
  });
32030
32107
  }
32031
- const sspPubIdentityKey = hexToBytes12(this.config.getSspIdentityPublicKey());
32108
+ const sspPubIdentityKey = hexToBytes11(this.config.getSspIdentityPublicKey());
32032
32109
  const transfer = await this.coopExitService.getConnectorRefundSignatures({
32033
32110
  leaves: leafKeyTweaks,
32034
32111
  exitTxId: coopExitTxId,
@@ -32065,8 +32142,7 @@ var SparkWallet = class extends EventEmitter {
32065
32142
  (await this.selectLeaves([amountSats])).get(amountSats),
32066
32143
  `no leaves for ${amountSats}`
32067
32144
  );
32068
- leaves = await this.checkRefreshTimelockNodes(leaves);
32069
- leaves = await this.checkExtendTimeLockNodes(leaves);
32145
+ leaves = await this.checkRenewLeaves(leaves);
32070
32146
  const feeEstimate = await sspClient.getCoopExitFeeQuote({
32071
32147
  leafExternalIds: leaves.map((leaf) => leaf.id),
32072
32148
  withdrawalAddress
@@ -32341,7 +32417,7 @@ var SparkWallet = class extends EventEmitter {
32341
32417
  async validateMessageWithIdentityKey(message, signature) {
32342
32418
  const hash = sha25611(message);
32343
32419
  if (typeof signature === "string") {
32344
- signature = hexToBytes12(signature);
32420
+ signature = hexToBytes11(signature);
32345
32421
  }
32346
32422
  return this.config.signer.validateMessageWithIdentityKey(hash, signature);
32347
32423
  }
@@ -32354,7 +32430,7 @@ var SparkWallet = class extends EventEmitter {
32354
32430
  */
32355
32431
  async signTransaction(txHex, keyType = "auto-detect") {
32356
32432
  try {
32357
- const tx = Transaction8.fromRaw(hexToBytes12(txHex));
32433
+ const tx = Transaction6.fromRaw(hexToBytes11(txHex));
32358
32434
  let publicKey;
32359
32435
  switch (keyType.toLowerCase()) {
32360
32436
  case "identity":
@@ -32561,15 +32637,6 @@ var SparkWallet = class extends EventEmitter {
32561
32637
  }
32562
32638
  );
32563
32639
  }
32564
- if (!nodeInput.sequence) {
32565
- throw new ValidationError(
32566
- `Node transaction has no sequence for ${isRootNode ? "root" : "non-root"} node`,
32567
- {
32568
- field: "sequence",
32569
- value: nodeInput.sequence
32570
- }
32571
- );
32572
- }
32573
32640
  const refundInput = refundTx.getInput(0);
32574
32641
  if (!refundInput) {
32575
32642
  throw new ValidationError(
@@ -32605,89 +32672,6 @@ var SparkWallet = class extends EventEmitter {
32605
32672
  );
32606
32673
  }
32607
32674
  }
32608
- /**
32609
- * Refresh the timelock of a specific node.
32610
- *
32611
- * @param {string} nodeId - The ID of the node to refresh
32612
- * @returns {Promise<void>} Promise that resolves when the timelock is refreshed
32613
- */
32614
- async testOnly_expireTimelock(nodeId) {
32615
- const sparkClient = await this.connectionManager.createSparkClient(
32616
- this.config.getCoordinatorAddress()
32617
- );
32618
- try {
32619
- const response = await sparkClient.query_nodes({
32620
- source: {
32621
- $case: "nodeIds",
32622
- nodeIds: {
32623
- nodeIds: [nodeId]
32624
- }
32625
- },
32626
- includeParents: true
32627
- });
32628
- let leaf = response.nodes[nodeId];
32629
- if (!leaf) {
32630
- throw new ValidationError("Node not found", {
32631
- field: "nodeId",
32632
- value: nodeId
32633
- });
32634
- }
32635
- let parentNode;
32636
- let hasParentNode = false;
32637
- if (!leaf.parentNodeId) {
32638
- } else {
32639
- hasParentNode = true;
32640
- parentNode = response.nodes[leaf.parentNodeId];
32641
- if (!parentNode) {
32642
- throw new ValidationError("Parent node not found", {
32643
- field: "parentNodeId",
32644
- value: leaf.parentNodeId
32645
- });
32646
- }
32647
- }
32648
- const nodeTx = getTxFromRawTxBytes(leaf.nodeTx);
32649
- const refundTx = getTxFromRawTxBytes(leaf.refundTx);
32650
- if (hasParentNode) {
32651
- const nodeTimelock = getCurrentTimelock(nodeTx.getInput(0).sequence);
32652
- if (nodeTimelock > 100) {
32653
- const expiredNodeTxLeaf = await this.transferService.testonly_expireTimeLockNodeTx(
32654
- leaf,
32655
- parentNode
32656
- );
32657
- if (!expiredNodeTxLeaf.nodes[0]) {
32658
- throw new ValidationError("No expired node tx leaf", {
32659
- field: "expiredNodeTxLeaf",
32660
- value: expiredNodeTxLeaf
32661
- });
32662
- }
32663
- leaf = expiredNodeTxLeaf.nodes[0];
32664
- }
32665
- }
32666
- const refundTimelock = getCurrentTimelock(refundTx.getInput(0).sequence);
32667
- if (refundTimelock > 100) {
32668
- const expiredTxLeaf = await this.transferService.testonly_expireTimeLockRefundtx(leaf);
32669
- if (!expiredTxLeaf.nodes[0]) {
32670
- throw new ValidationError("No expired tx leaf", {
32671
- field: "expiredTxLeaf",
32672
- value: expiredTxLeaf
32673
- });
32674
- }
32675
- leaf = expiredTxLeaf.nodes[0];
32676
- }
32677
- const leafIndex = this.leaves.findIndex((leaf2) => leaf2.id === leaf2.id);
32678
- if (leafIndex !== -1) {
32679
- this.leaves[leafIndex] = leaf;
32680
- }
32681
- } catch (error) {
32682
- throw new NetworkError(
32683
- "Failed to refresh timelock",
32684
- {
32685
- method: "refresh_timelock"
32686
- },
32687
- error
32688
- );
32689
- }
32690
- }
32691
32675
  cleanup() {
32692
32676
  if (this.claimTransfersInterval) {
32693
32677
  clearInterval(this.claimTransfersInterval);
@@ -32842,8 +32826,7 @@ var SparkWallet = class extends EventEmitter {
32842
32826
  "getLightningReceiveRequest",
32843
32827
  "getLightningSendRequest",
32844
32828
  "getCoopExitRequest",
32845
- "checkTimelock",
32846
- "testOnly_expireTimelock"
32829
+ "checkTimelock"
32847
32830
  ];
32848
32831
  methods.forEach((m) => this.wrapPublicSparkWalletMethodWithOtelSpan(m));
32849
32832
  this.initWallet = this.wrapWithOtelSpan(
@@ -33905,7 +33888,7 @@ function collectResponses(responses) {
33905
33888
  }
33906
33889
 
33907
33890
  // src/utils/unilateral-exit.ts
33908
- import { bytesToHex as bytesToHex12, hexToBytes as hexToBytes13 } from "@noble/curves/utils";
33891
+ import { bytesToHex as bytesToHex12, hexToBytes as hexToBytes12 } from "@noble/curves/utils";
33909
33892
  import { ripemd160 } from "@noble/hashes/legacy";
33910
33893
  import { sha256 as sha25613 } from "@noble/hashes/sha2";
33911
33894
  import * as btc4 from "@scure/btc-signer";
@@ -33920,7 +33903,7 @@ function isEphemeralAnchorOutput(script, amount) {
33920
33903
  }
33921
33904
  async function constructUnilateralExitTxs(nodeHexStrings, sparkClient, network) {
33922
33905
  const result = [];
33923
- const nodes = nodeHexStrings.map((hex) => TreeNode.decode(hexToBytes13(hex)));
33906
+ const nodes = nodeHexStrings.map((hex) => TreeNode.decode(hexToBytes12(hex)));
33924
33907
  const nodeMap = /* @__PURE__ */ new Map();
33925
33908
  for (const node of nodes) {
33926
33909
  nodeMap.set(node.id, node);
@@ -34009,7 +33992,7 @@ async function constructUnilateralExitFeeBumpPackages(nodeHexStrings, utxos, fee
34009
33992
  `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.`
34010
33993
  );
34011
33994
  }
34012
- const nodeBytes = hexToBytes13(hex);
33995
+ const nodeBytes = hexToBytes12(hex);
34013
33996
  const node = TreeNode.decode(nodeBytes);
34014
33997
  if (!node.id) {
34015
33998
  throw new Error(
@@ -34123,7 +34106,7 @@ async function constructUnilateralExitFeeBumpPackages(nodeHexStrings, utxos, fee
34123
34106
  usedUtxos,
34124
34107
  correctedParentTx
34125
34108
  } = constructFeeBumpTx(nodeTxHex, availableUtxos, feeRate, void 0);
34126
- const feeBumpTx = btc4.Transaction.fromPSBT(hexToBytes13(nodeFeeBumpPsbt));
34109
+ const feeBumpTx = btc4.Transaction.fromPSBT(hexToBytes12(nodeFeeBumpPsbt));
34127
34110
  var feeBumpOut = feeBumpTx.outputsLength === 1 ? feeBumpTx.getOutput(0) : null;
34128
34111
  var feeBumpOutPubKey = null;
34129
34112
  for (const usedUtxo of usedUtxos) {
@@ -34178,7 +34161,7 @@ async function constructUnilateralExitFeeBumpPackages(nodeHexStrings, utxos, fee
34178
34161
  void 0
34179
34162
  );
34180
34163
  const feeBumpTx2 = btc4.Transaction.fromPSBT(
34181
- hexToBytes13(refundFeeBump.feeBumpPsbt)
34164
+ hexToBytes12(refundFeeBump.feeBumpPsbt)
34182
34165
  );
34183
34166
  var feeBumpOut = feeBumpTx2.outputsLength === 1 ? feeBumpTx2.getOutput(0) : null;
34184
34167
  var feeBumpOutPubKey = null;
@@ -34293,9 +34276,9 @@ function constructFeeBumpTx(txHex, utxos, feeRate, previousFeeBumpTx) {
34293
34276
  if (!fundingUtxo) {
34294
34277
  throw new Error(`UTXO at index ${i} is undefined`);
34295
34278
  }
34296
- const pubKeyHash = hash160(hexToBytes13(fundingUtxo.publicKey));
34279
+ const pubKeyHash = hash160(hexToBytes12(fundingUtxo.publicKey));
34297
34280
  const scriptToUse = new Uint8Array([0, 20, ...pubKeyHash]);
34298
- const providedScript = hexToBytes13(fundingUtxo.script);
34281
+ const providedScript = hexToBytes12(fundingUtxo.script);
34299
34282
  if (bytesToHex12(scriptToUse) !== bytesToHex12(providedScript)) {
34300
34283
  throw new Error(
34301
34284
  `\u274C Derived script doesn't match provided script for UTXO ${i + 1}.`
@@ -34423,7 +34406,6 @@ export {
34423
34406
  DIRECT_TIMELOCK_OFFSET,
34424
34407
  DefaultSparkSigner,
34425
34408
  HTLC_TIMELOCK_OFFSET,
34426
- INITIAL_DIRECT_SEQUENCE,
34427
34409
  INITIAL_SEQUENCE,
34428
34410
  InternalValidationError,
34429
34411
  LOGGER_NAMES,
@@ -34456,21 +34438,24 @@ export {
34456
34438
  constructFeeBumpTx,
34457
34439
  constructUnilateralExitFeeBumpPackages,
34458
34440
  constructUnilateralExitTxs,
34459
- createConnectorRefundTransactions,
34460
- createLeafNodeTx,
34461
- createNodeTx,
34462
- createNodeTxs,
34463
- createRefundTx,
34464
- createRefundTxs,
34465
- createRootTx,
34441
+ createConnectorRefundTxs,
34442
+ createCurrentTimelockRefundTxs,
34443
+ createDecrementedTimelockNodeTx,
34444
+ createDecrementedTimelockRefundTxs,
34445
+ createInitialTimelockNodeTx,
34446
+ createInitialTimelockRefundTxs,
34447
+ createRootNodeTx,
34466
34448
  createSigningCommitment,
34467
34449
  createSigningNonce,
34468
- createSplitTx,
34450
+ createTestUnilateralRefundTxs,
34451
+ createTestUnilateralTimelockNodeTx,
34452
+ createZeroTimelockNodeTx,
34469
34453
  decodeBech32mTokenIdentifier,
34470
34454
  decodeBytesToSigningCommitment,
34471
34455
  decodeBytesToSigningNonce,
34472
34456
  decodeSparkAddress,
34473
34457
  doesLeafNeedRefresh,
34458
+ doesTxnNeedRenewed,
34474
34459
  encodeBech32mTokenIdentifier,
34475
34460
  encodeSigningCommitmentToBytes,
34476
34461
  encodeSigningNonceToBytes,
@@ -34508,12 +34493,14 @@ export {
34508
34493
  getTxFromRawTxHex,
34509
34494
  getTxId,
34510
34495
  getTxIdNoReverse,
34496
+ hash160,
34511
34497
  isEphemeralAnchorOutput,
34512
34498
  isLegacySparkAddress,
34513
34499
  isSafeForNumber,
34514
34500
  isTxBroadcast,
34515
34501
  isValidPublicKey,
34516
34502
  isValidSparkAddress,
34503
+ isZeroTimelock,
34517
34504
  lastKeyWithTarget,
34518
34505
  maybeApplyFee,
34519
34506
  modInverse,