@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
@@ -1228,7 +1228,6 @@ __export(index_exports, {
1228
1228
  DIRECT_TIMELOCK_OFFSET: () => DIRECT_TIMELOCK_OFFSET,
1229
1229
  DefaultSparkSigner: () => DefaultSparkSigner,
1230
1230
  HTLC_TIMELOCK_OFFSET: () => HTLC_TIMELOCK_OFFSET,
1231
- INITIAL_DIRECT_SEQUENCE: () => INITIAL_DIRECT_SEQUENCE,
1232
1231
  INITIAL_SEQUENCE: () => INITIAL_SEQUENCE,
1233
1232
  InternalValidationError: () => InternalValidationError,
1234
1233
  LOGGER_NAMES: () => LOGGER_NAMES,
@@ -1261,21 +1260,24 @@ __export(index_exports, {
1261
1260
  constructFeeBumpTx: () => constructFeeBumpTx,
1262
1261
  constructUnilateralExitFeeBumpPackages: () => constructUnilateralExitFeeBumpPackages,
1263
1262
  constructUnilateralExitTxs: () => constructUnilateralExitTxs,
1264
- createConnectorRefundTransactions: () => createConnectorRefundTransactions,
1265
- createLeafNodeTx: () => createLeafNodeTx,
1266
- createNodeTx: () => createNodeTx,
1267
- createNodeTxs: () => createNodeTxs,
1268
- createRefundTx: () => createRefundTx,
1269
- createRefundTxs: () => createRefundTxs,
1270
- createRootTx: () => createRootTx,
1263
+ createConnectorRefundTxs: () => createConnectorRefundTxs,
1264
+ createCurrentTimelockRefundTxs: () => createCurrentTimelockRefundTxs,
1265
+ createDecrementedTimelockNodeTx: () => createDecrementedTimelockNodeTx,
1266
+ createDecrementedTimelockRefundTxs: () => createDecrementedTimelockRefundTxs,
1267
+ createInitialTimelockNodeTx: () => createInitialTimelockNodeTx,
1268
+ createInitialTimelockRefundTxs: () => createInitialTimelockRefundTxs,
1269
+ createRootNodeTx: () => createRootNodeTx,
1271
1270
  createSigningCommitment: () => createSigningCommitment,
1272
1271
  createSigningNonce: () => createSigningNonce,
1273
- createSplitTx: () => createSplitTx,
1272
+ createTestUnilateralRefundTxs: () => createTestUnilateralRefundTxs,
1273
+ createTestUnilateralTimelockNodeTx: () => createTestUnilateralTimelockNodeTx,
1274
+ createZeroTimelockNodeTx: () => createZeroTimelockNodeTx,
1274
1275
  decodeBech32mTokenIdentifier: () => decodeBech32mTokenIdentifier,
1275
1276
  decodeBytesToSigningCommitment: () => decodeBytesToSigningCommitment,
1276
1277
  decodeBytesToSigningNonce: () => decodeBytesToSigningNonce,
1277
1278
  decodeSparkAddress: () => decodeSparkAddress,
1278
1279
  doesLeafNeedRefresh: () => doesLeafNeedRefresh,
1280
+ doesTxnNeedRenewed: () => doesTxnNeedRenewed,
1279
1281
  encodeBech32mTokenIdentifier: () => encodeBech32mTokenIdentifier,
1280
1282
  encodeSigningCommitmentToBytes: () => encodeSigningCommitmentToBytes,
1281
1283
  encodeSigningNonceToBytes: () => encodeSigningNonceToBytes,
@@ -1313,12 +1315,14 @@ __export(index_exports, {
1313
1315
  getTxFromRawTxHex: () => getTxFromRawTxHex,
1314
1316
  getTxId: () => getTxId,
1315
1317
  getTxIdNoReverse: () => getTxIdNoReverse,
1318
+ hash160: () => hash160,
1316
1319
  isEphemeralAnchorOutput: () => isEphemeralAnchorOutput,
1317
1320
  isLegacySparkAddress: () => isLegacySparkAddress,
1318
1321
  isSafeForNumber: () => isSafeForNumber,
1319
1322
  isTxBroadcast: () => isTxBroadcast,
1320
1323
  isValidPublicKey: () => isValidPublicKey,
1321
1324
  isValidSparkAddress: () => isValidSparkAddress,
1325
+ isZeroTimelock: () => isZeroTimelock,
1322
1326
  lastKeyWithTarget: () => lastKeyWithTarget,
1323
1327
  maybeApplyFee: () => maybeApplyFee,
1324
1328
  modInverse: () => modInverse,
@@ -1502,7 +1506,7 @@ var import_secp256k114 = require("@noble/curves/secp256k1");
1502
1506
  var import_utils24 = require("@noble/curves/utils");
1503
1507
  var import_bip392 = require("@scure/bip39");
1504
1508
  var import_english2 = require("@scure/bip39/wordlists/english");
1505
- var import_btc_signer7 = require("@scure/btc-signer");
1509
+ var import_btc_signer5 = require("@scure/btc-signer");
1506
1510
  var import_async_mutex = require("async-mutex");
1507
1511
  var import_uuidv75 = require("uuidv7");
1508
1512
 
@@ -5949,6 +5953,12 @@ var RenewLeafRequest = {
5949
5953
  writer.uint32(26).fork()
5950
5954
  ).join();
5951
5955
  break;
5956
+ case "renewNodeZeroTimelockSigningJob":
5957
+ RenewNodeZeroTimelockSigningJob.encode(
5958
+ message.signingJobs.renewNodeZeroTimelockSigningJob,
5959
+ writer.uint32(34).fork()
5960
+ ).join();
5961
+ break;
5952
5962
  }
5953
5963
  return writer;
5954
5964
  },
@@ -5986,6 +5996,16 @@ var RenewLeafRequest = {
5986
5996
  };
5987
5997
  continue;
5988
5998
  }
5999
+ case 4: {
6000
+ if (tag !== 34) {
6001
+ break;
6002
+ }
6003
+ message.signingJobs = {
6004
+ $case: "renewNodeZeroTimelockSigningJob",
6005
+ renewNodeZeroTimelockSigningJob: RenewNodeZeroTimelockSigningJob.decode(reader, reader.uint32())
6006
+ };
6007
+ continue;
6008
+ }
5989
6009
  }
5990
6010
  if ((tag & 7) === 4 || tag === 0) {
5991
6011
  break;
@@ -6003,6 +6023,11 @@ var RenewLeafRequest = {
6003
6023
  } : isSet3(object.renewRefundTimelockSigningJob) ? {
6004
6024
  $case: "renewRefundTimelockSigningJob",
6005
6025
  renewRefundTimelockSigningJob: RenewRefundTimelockSigningJob.fromJSON(object.renewRefundTimelockSigningJob)
6026
+ } : isSet3(object.renewNodeZeroTimelockSigningJob) ? {
6027
+ $case: "renewNodeZeroTimelockSigningJob",
6028
+ renewNodeZeroTimelockSigningJob: RenewNodeZeroTimelockSigningJob.fromJSON(
6029
+ object.renewNodeZeroTimelockSigningJob
6030
+ )
6006
6031
  } : void 0
6007
6032
  };
6008
6033
  },
@@ -6019,6 +6044,10 @@ var RenewLeafRequest = {
6019
6044
  obj.renewRefundTimelockSigningJob = RenewRefundTimelockSigningJob.toJSON(
6020
6045
  message.signingJobs.renewRefundTimelockSigningJob
6021
6046
  );
6047
+ } else if (message.signingJobs?.$case === "renewNodeZeroTimelockSigningJob") {
6048
+ obj.renewNodeZeroTimelockSigningJob = RenewNodeZeroTimelockSigningJob.toJSON(
6049
+ message.signingJobs.renewNodeZeroTimelockSigningJob
6050
+ );
6022
6051
  }
6023
6052
  return obj;
6024
6053
  },
@@ -6051,6 +6080,17 @@ var RenewLeafRequest = {
6051
6080
  }
6052
6081
  break;
6053
6082
  }
6083
+ case "renewNodeZeroTimelockSigningJob": {
6084
+ if (object.signingJobs?.renewNodeZeroTimelockSigningJob !== void 0 && object.signingJobs?.renewNodeZeroTimelockSigningJob !== null) {
6085
+ message.signingJobs = {
6086
+ $case: "renewNodeZeroTimelockSigningJob",
6087
+ renewNodeZeroTimelockSigningJob: RenewNodeZeroTimelockSigningJob.fromPartial(
6088
+ object.signingJobs.renewNodeZeroTimelockSigningJob
6089
+ )
6090
+ };
6091
+ }
6092
+ break;
6093
+ }
6054
6094
  }
6055
6095
  return message;
6056
6096
  }
@@ -6325,6 +6365,125 @@ var RenewRefundTimelockSigningJob = {
6325
6365
  return message;
6326
6366
  }
6327
6367
  };
6368
+ function createBaseRenewNodeZeroTimelockSigningJob() {
6369
+ return {
6370
+ nodeTxSigningJob: void 0,
6371
+ refundTxSigningJob: void 0,
6372
+ directNodeTxSigningJob: void 0,
6373
+ directRefundTxSigningJob: void 0,
6374
+ directFromCpfpRefundTxSigningJob: void 0
6375
+ };
6376
+ }
6377
+ var RenewNodeZeroTimelockSigningJob = {
6378
+ encode(message, writer = new import_wire4.BinaryWriter()) {
6379
+ if (message.nodeTxSigningJob !== void 0) {
6380
+ UserSignedTxSigningJob.encode(message.nodeTxSigningJob, writer.uint32(10).fork()).join();
6381
+ }
6382
+ if (message.refundTxSigningJob !== void 0) {
6383
+ UserSignedTxSigningJob.encode(message.refundTxSigningJob, writer.uint32(18).fork()).join();
6384
+ }
6385
+ if (message.directNodeTxSigningJob !== void 0) {
6386
+ UserSignedTxSigningJob.encode(message.directNodeTxSigningJob, writer.uint32(26).fork()).join();
6387
+ }
6388
+ if (message.directRefundTxSigningJob !== void 0) {
6389
+ UserSignedTxSigningJob.encode(message.directRefundTxSigningJob, writer.uint32(34).fork()).join();
6390
+ }
6391
+ if (message.directFromCpfpRefundTxSigningJob !== void 0) {
6392
+ UserSignedTxSigningJob.encode(message.directFromCpfpRefundTxSigningJob, writer.uint32(42).fork()).join();
6393
+ }
6394
+ return writer;
6395
+ },
6396
+ decode(input, length) {
6397
+ const reader = input instanceof import_wire4.BinaryReader ? input : new import_wire4.BinaryReader(input);
6398
+ const end = length === void 0 ? reader.len : reader.pos + length;
6399
+ const message = createBaseRenewNodeZeroTimelockSigningJob();
6400
+ while (reader.pos < end) {
6401
+ const tag = reader.uint32();
6402
+ switch (tag >>> 3) {
6403
+ case 1: {
6404
+ if (tag !== 10) {
6405
+ break;
6406
+ }
6407
+ message.nodeTxSigningJob = UserSignedTxSigningJob.decode(reader, reader.uint32());
6408
+ continue;
6409
+ }
6410
+ case 2: {
6411
+ if (tag !== 18) {
6412
+ break;
6413
+ }
6414
+ message.refundTxSigningJob = UserSignedTxSigningJob.decode(reader, reader.uint32());
6415
+ continue;
6416
+ }
6417
+ case 3: {
6418
+ if (tag !== 26) {
6419
+ break;
6420
+ }
6421
+ message.directNodeTxSigningJob = UserSignedTxSigningJob.decode(reader, reader.uint32());
6422
+ continue;
6423
+ }
6424
+ case 4: {
6425
+ if (tag !== 34) {
6426
+ break;
6427
+ }
6428
+ message.directRefundTxSigningJob = UserSignedTxSigningJob.decode(reader, reader.uint32());
6429
+ continue;
6430
+ }
6431
+ case 5: {
6432
+ if (tag !== 42) {
6433
+ break;
6434
+ }
6435
+ message.directFromCpfpRefundTxSigningJob = UserSignedTxSigningJob.decode(reader, reader.uint32());
6436
+ continue;
6437
+ }
6438
+ }
6439
+ if ((tag & 7) === 4 || tag === 0) {
6440
+ break;
6441
+ }
6442
+ reader.skip(tag & 7);
6443
+ }
6444
+ return message;
6445
+ },
6446
+ fromJSON(object) {
6447
+ return {
6448
+ nodeTxSigningJob: isSet3(object.nodeTxSigningJob) ? UserSignedTxSigningJob.fromJSON(object.nodeTxSigningJob) : void 0,
6449
+ refundTxSigningJob: isSet3(object.refundTxSigningJob) ? UserSignedTxSigningJob.fromJSON(object.refundTxSigningJob) : void 0,
6450
+ directNodeTxSigningJob: isSet3(object.directNodeTxSigningJob) ? UserSignedTxSigningJob.fromJSON(object.directNodeTxSigningJob) : void 0,
6451
+ directRefundTxSigningJob: isSet3(object.directRefundTxSigningJob) ? UserSignedTxSigningJob.fromJSON(object.directRefundTxSigningJob) : void 0,
6452
+ directFromCpfpRefundTxSigningJob: isSet3(object.directFromCpfpRefundTxSigningJob) ? UserSignedTxSigningJob.fromJSON(object.directFromCpfpRefundTxSigningJob) : void 0
6453
+ };
6454
+ },
6455
+ toJSON(message) {
6456
+ const obj = {};
6457
+ if (message.nodeTxSigningJob !== void 0) {
6458
+ obj.nodeTxSigningJob = UserSignedTxSigningJob.toJSON(message.nodeTxSigningJob);
6459
+ }
6460
+ if (message.refundTxSigningJob !== void 0) {
6461
+ obj.refundTxSigningJob = UserSignedTxSigningJob.toJSON(message.refundTxSigningJob);
6462
+ }
6463
+ if (message.directNodeTxSigningJob !== void 0) {
6464
+ obj.directNodeTxSigningJob = UserSignedTxSigningJob.toJSON(message.directNodeTxSigningJob);
6465
+ }
6466
+ if (message.directRefundTxSigningJob !== void 0) {
6467
+ obj.directRefundTxSigningJob = UserSignedTxSigningJob.toJSON(message.directRefundTxSigningJob);
6468
+ }
6469
+ if (message.directFromCpfpRefundTxSigningJob !== void 0) {
6470
+ obj.directFromCpfpRefundTxSigningJob = UserSignedTxSigningJob.toJSON(message.directFromCpfpRefundTxSigningJob);
6471
+ }
6472
+ return obj;
6473
+ },
6474
+ create(base) {
6475
+ return RenewNodeZeroTimelockSigningJob.fromPartial(base ?? {});
6476
+ },
6477
+ fromPartial(object) {
6478
+ const message = createBaseRenewNodeZeroTimelockSigningJob();
6479
+ message.nodeTxSigningJob = object.nodeTxSigningJob !== void 0 && object.nodeTxSigningJob !== null ? UserSignedTxSigningJob.fromPartial(object.nodeTxSigningJob) : void 0;
6480
+ message.refundTxSigningJob = object.refundTxSigningJob !== void 0 && object.refundTxSigningJob !== null ? UserSignedTxSigningJob.fromPartial(object.refundTxSigningJob) : void 0;
6481
+ message.directNodeTxSigningJob = object.directNodeTxSigningJob !== void 0 && object.directNodeTxSigningJob !== null ? UserSignedTxSigningJob.fromPartial(object.directNodeTxSigningJob) : void 0;
6482
+ message.directRefundTxSigningJob = object.directRefundTxSigningJob !== void 0 && object.directRefundTxSigningJob !== null ? UserSignedTxSigningJob.fromPartial(object.directRefundTxSigningJob) : void 0;
6483
+ message.directFromCpfpRefundTxSigningJob = object.directFromCpfpRefundTxSigningJob !== void 0 && object.directFromCpfpRefundTxSigningJob !== null ? UserSignedTxSigningJob.fromPartial(object.directFromCpfpRefundTxSigningJob) : void 0;
6484
+ return message;
6485
+ }
6486
+ };
6328
6487
  function createBaseRenewLeafResponse() {
6329
6488
  return { renewResult: void 0 };
6330
6489
  }
@@ -6337,6 +6496,9 @@ var RenewLeafResponse = {
6337
6496
  case "renewRefundTimelockResult":
6338
6497
  RenewRefundTimelockResult.encode(message.renewResult.renewRefundTimelockResult, writer.uint32(18).fork()).join();
6339
6498
  break;
6499
+ case "renewNodeZeroTimelockResult":
6500
+ RenewNodeZeroTimelockResult.encode(message.renewResult.renewNodeZeroTimelockResult, writer.uint32(26).fork()).join();
6501
+ break;
6340
6502
  }
6341
6503
  return writer;
6342
6504
  },
@@ -6367,6 +6529,16 @@ var RenewLeafResponse = {
6367
6529
  };
6368
6530
  continue;
6369
6531
  }
6532
+ case 3: {
6533
+ if (tag !== 26) {
6534
+ break;
6535
+ }
6536
+ message.renewResult = {
6537
+ $case: "renewNodeZeroTimelockResult",
6538
+ renewNodeZeroTimelockResult: RenewNodeZeroTimelockResult.decode(reader, reader.uint32())
6539
+ };
6540
+ continue;
6541
+ }
6370
6542
  }
6371
6543
  if ((tag & 7) === 4 || tag === 0) {
6372
6544
  break;
@@ -6383,6 +6555,9 @@ var RenewLeafResponse = {
6383
6555
  } : isSet3(object.renewRefundTimelockResult) ? {
6384
6556
  $case: "renewRefundTimelockResult",
6385
6557
  renewRefundTimelockResult: RenewRefundTimelockResult.fromJSON(object.renewRefundTimelockResult)
6558
+ } : isSet3(object.renewNodeZeroTimelockResult) ? {
6559
+ $case: "renewNodeZeroTimelockResult",
6560
+ renewNodeZeroTimelockResult: RenewNodeZeroTimelockResult.fromJSON(object.renewNodeZeroTimelockResult)
6386
6561
  } : void 0
6387
6562
  };
6388
6563
  },
@@ -6392,6 +6567,10 @@ var RenewLeafResponse = {
6392
6567
  obj.renewNodeTimelockResult = RenewNodeTimelockResult.toJSON(message.renewResult.renewNodeTimelockResult);
6393
6568
  } else if (message.renewResult?.$case === "renewRefundTimelockResult") {
6394
6569
  obj.renewRefundTimelockResult = RenewRefundTimelockResult.toJSON(message.renewResult.renewRefundTimelockResult);
6570
+ } else if (message.renewResult?.$case === "renewNodeZeroTimelockResult") {
6571
+ obj.renewNodeZeroTimelockResult = RenewNodeZeroTimelockResult.toJSON(
6572
+ message.renewResult.renewNodeZeroTimelockResult
6573
+ );
6395
6574
  }
6396
6575
  return obj;
6397
6576
  },
@@ -6421,6 +6600,17 @@ var RenewLeafResponse = {
6421
6600
  }
6422
6601
  break;
6423
6602
  }
6603
+ case "renewNodeZeroTimelockResult": {
6604
+ if (object.renewResult?.renewNodeZeroTimelockResult !== void 0 && object.renewResult?.renewNodeZeroTimelockResult !== null) {
6605
+ message.renewResult = {
6606
+ $case: "renewNodeZeroTimelockResult",
6607
+ renewNodeZeroTimelockResult: RenewNodeZeroTimelockResult.fromPartial(
6608
+ object.renewResult.renewNodeZeroTimelockResult
6609
+ )
6610
+ };
6611
+ }
6612
+ break;
6613
+ }
6424
6614
  }
6425
6615
  return message;
6426
6616
  }
@@ -6544,6 +6734,74 @@ var RenewRefundTimelockResult = {
6544
6734
  return message;
6545
6735
  }
6546
6736
  };
6737
+ function createBaseRenewNodeZeroTimelockResult() {
6738
+ return { splitNode: void 0, node: void 0 };
6739
+ }
6740
+ var RenewNodeZeroTimelockResult = {
6741
+ encode(message, writer = new import_wire4.BinaryWriter()) {
6742
+ if (message.splitNode !== void 0) {
6743
+ TreeNode.encode(message.splitNode, writer.uint32(10).fork()).join();
6744
+ }
6745
+ if (message.node !== void 0) {
6746
+ TreeNode.encode(message.node, writer.uint32(18).fork()).join();
6747
+ }
6748
+ return writer;
6749
+ },
6750
+ decode(input, length) {
6751
+ const reader = input instanceof import_wire4.BinaryReader ? input : new import_wire4.BinaryReader(input);
6752
+ const end = length === void 0 ? reader.len : reader.pos + length;
6753
+ const message = createBaseRenewNodeZeroTimelockResult();
6754
+ while (reader.pos < end) {
6755
+ const tag = reader.uint32();
6756
+ switch (tag >>> 3) {
6757
+ case 1: {
6758
+ if (tag !== 10) {
6759
+ break;
6760
+ }
6761
+ message.splitNode = TreeNode.decode(reader, reader.uint32());
6762
+ continue;
6763
+ }
6764
+ case 2: {
6765
+ if (tag !== 18) {
6766
+ break;
6767
+ }
6768
+ message.node = TreeNode.decode(reader, reader.uint32());
6769
+ continue;
6770
+ }
6771
+ }
6772
+ if ((tag & 7) === 4 || tag === 0) {
6773
+ break;
6774
+ }
6775
+ reader.skip(tag & 7);
6776
+ }
6777
+ return message;
6778
+ },
6779
+ fromJSON(object) {
6780
+ return {
6781
+ splitNode: isSet3(object.splitNode) ? TreeNode.fromJSON(object.splitNode) : void 0,
6782
+ node: isSet3(object.node) ? TreeNode.fromJSON(object.node) : void 0
6783
+ };
6784
+ },
6785
+ toJSON(message) {
6786
+ const obj = {};
6787
+ if (message.splitNode !== void 0) {
6788
+ obj.splitNode = TreeNode.toJSON(message.splitNode);
6789
+ }
6790
+ if (message.node !== void 0) {
6791
+ obj.node = TreeNode.toJSON(message.node);
6792
+ }
6793
+ return obj;
6794
+ },
6795
+ create(base) {
6796
+ return RenewNodeZeroTimelockResult.fromPartial(base ?? {});
6797
+ },
6798
+ fromPartial(object) {
6799
+ const message = createBaseRenewNodeZeroTimelockResult();
6800
+ message.splitNode = object.splitNode !== void 0 && object.splitNode !== null ? TreeNode.fromPartial(object.splitNode) : void 0;
6801
+ message.node = object.node !== void 0 && object.node !== null ? TreeNode.fromPartial(object.node) : void 0;
6802
+ return message;
6803
+ }
6804
+ };
6547
6805
  function createBaseNodeSignatureShares() {
6548
6806
  return {
6549
6807
  nodeId: "",
@@ -19298,7 +19556,11 @@ var BASE_CONFIG = {
19298
19556
  console: {
19299
19557
  otel: false
19300
19558
  },
19301
- events: {}
19559
+ events: {},
19560
+ optimizationOptions: {
19561
+ auto: true,
19562
+ multiplicity: 0
19563
+ }
19302
19564
  };
19303
19565
  var LOCAL_WALLET_CONFIG = {
19304
19566
  ...BASE_CONFIG,
@@ -19509,10 +19771,12 @@ var WalletConfigService = class {
19509
19771
  getEvents() {
19510
19772
  return this.config.events;
19511
19773
  }
19774
+ getOptimizationOptions() {
19775
+ return this.config.optimizationOptions;
19776
+ }
19512
19777
  };
19513
19778
 
19514
19779
  // src/services/coop-exit.ts
19515
- var import_btc_signer3 = require("@scure/btc-signer");
19516
19780
  var import_uuidv72 = require("uuidv7");
19517
19781
 
19518
19782
  // src/utils/bitcoin.ts
@@ -19667,6 +19931,7 @@ function getTxEstimatedVbytesSizeByNumberOfInputsOutputs(numInputs, numOutputs)
19667
19931
  }
19668
19932
 
19669
19933
  // src/utils/transaction.ts
19934
+ var import_utils3 = require("@noble/hashes/utils");
19670
19935
  var import_btc_signer = require("@scure/btc-signer");
19671
19936
  var INITIAL_TIMELOCK = 2e3;
19672
19937
  var TEST_UNILATERAL_TIMELOCK = 100;
@@ -19675,9 +19940,9 @@ var DIRECT_TIMELOCK_OFFSET = 50;
19675
19940
  var HTLC_TIMELOCK_OFFSET = 70;
19676
19941
  var DIRECT_HTLC_TIMELOCK_OFFSET = 85;
19677
19942
  var INITIAL_SEQUENCE = 1 << 30 | INITIAL_TIMELOCK;
19678
- var INITIAL_DIRECT_SEQUENCE = 1 << 30 | INITIAL_TIMELOCK + DIRECT_TIMELOCK_OFFSET;
19679
19943
  var TEST_UNILATERAL_SEQUENCE = 1 << 30 | TEST_UNILATERAL_TIMELOCK;
19680
19944
  var TEST_UNILATERAL_DIRECT_SEQUENCE = 1 << 30 | TEST_UNILATERAL_TIMELOCK + DIRECT_TIMELOCK_OFFSET;
19945
+ var INITIAL_ROOT_NODE_SEQUENCE = 1 << 30 | 0;
19681
19946
  var ESTIMATED_TX_SIZE = 191;
19682
19947
  var DEFAULT_SATS_PER_VBYTE = 5;
19683
19948
  var DEFAULT_FEE_SATS = ESTIMATED_TX_SIZE * DEFAULT_SATS_PER_VBYTE;
@@ -19687,63 +19952,8 @@ function maybeApplyFee(amount) {
19687
19952
  }
19688
19953
  return amount;
19689
19954
  }
19690
- function createRootTx(depositOutPoint, depositTxOut) {
19691
- const cpfpRootTx = new import_btc_signer.Transaction({
19692
- version: 3,
19693
- allowUnknownOutputs: true
19694
- });
19695
- cpfpRootTx.addInput(depositOutPoint);
19696
- cpfpRootTx.addOutput(depositTxOut);
19697
- cpfpRootTx.addOutput(getEphemeralAnchorOutput());
19698
- const directRootTx = new import_btc_signer.Transaction({
19699
- version: 3,
19700
- allowUnknownOutputs: true
19701
- });
19702
- directRootTx.addInput(depositOutPoint);
19703
- directRootTx.addOutput({
19704
- script: depositTxOut.script,
19705
- amount: maybeApplyFee(depositTxOut.amount ?? 0n)
19706
- });
19707
- return [cpfpRootTx, directRootTx];
19708
- }
19709
- function createSplitTx(parentOutPoint, childTxOuts) {
19710
- const cpfpSplitTx = new import_btc_signer.Transaction({
19711
- version: 3,
19712
- allowUnknownOutputs: true
19713
- });
19714
- cpfpSplitTx.addInput(parentOutPoint);
19715
- for (const txOut of childTxOuts) {
19716
- cpfpSplitTx.addOutput(txOut);
19717
- }
19718
- cpfpSplitTx.addOutput(getEphemeralAnchorOutput());
19719
- const directSplitTx = new import_btc_signer.Transaction({
19720
- version: 3,
19721
- allowUnknownOutputs: true
19722
- });
19723
- directSplitTx.addInput(parentOutPoint);
19724
- let totalOutputAmount = 0n;
19725
- for (const txOut of childTxOuts) {
19726
- totalOutputAmount += txOut.amount ?? 0n;
19727
- }
19728
- if (totalOutputAmount > BigInt(DEFAULT_FEE_SATS)) {
19729
- const feeRatio = Number(DEFAULT_FEE_SATS) / Number(totalOutputAmount);
19730
- for (const txOut of childTxOuts) {
19731
- const adjustedAmount = BigInt(
19732
- Math.floor(Number(txOut.amount ?? 0n) * (1 - feeRatio))
19733
- );
19734
- directSplitTx.addOutput({
19735
- script: txOut.script,
19736
- amount: adjustedAmount
19737
- });
19738
- }
19739
- } else {
19740
- for (const txOut of childTxOuts) {
19741
- directSplitTx.addOutput(txOut);
19742
- }
19743
- }
19744
- return [cpfpSplitTx, directSplitTx];
19745
- }
19746
19955
  function createNodeTx({
19956
+ sequence,
19747
19957
  txOut,
19748
19958
  parentOutPoint,
19749
19959
  applyFee,
@@ -19753,7 +19963,10 @@ function createNodeTx({
19753
19963
  version: 3,
19754
19964
  allowUnknownOutputs: true
19755
19965
  });
19756
- nodeTx.addInput(parentOutPoint);
19966
+ nodeTx.addInput({
19967
+ ...parentOutPoint,
19968
+ sequence
19969
+ });
19757
19970
  if (applyFee) {
19758
19971
  nodeTx.addOutput({
19759
19972
  script: txOut.script,
@@ -19767,52 +19980,92 @@ function createNodeTx({
19767
19980
  }
19768
19981
  return nodeTx;
19769
19982
  }
19770
- function createNodeTxs(txOut, txIn, directTxIn) {
19771
- const cpfpNodeTx = createNodeTx({
19772
- txOut,
19773
- parentOutPoint: txIn,
19774
- includeAnchor: true
19775
- });
19776
- let directNodeTx;
19777
- if (directTxIn) {
19778
- directNodeTx = createNodeTx({
19779
- txOut,
19780
- parentOutPoint: directTxIn,
19781
- includeAnchor: false,
19782
- applyFee: true
19983
+ function createNodeTxs({
19984
+ parentTx,
19985
+ sequence,
19986
+ directSequence,
19987
+ vout
19988
+ }) {
19989
+ const parentOutput = parentTx.getOutput(vout);
19990
+ if (!parentOutput.amount || !parentOutput.script) {
19991
+ throw new ValidationError("Parent output amount or script not found", {
19992
+ field: "parentOutput",
19993
+ value: parentOutput
19783
19994
  });
19784
19995
  }
19785
- return { cpfpNodeTx, directNodeTx };
19996
+ const output = {
19997
+ script: parentOutput.script,
19998
+ amount: parentOutput.amount
19999
+ };
20000
+ const input = {
20001
+ txid: (0, import_utils3.hexToBytes)(getTxId(parentTx)),
20002
+ index: vout
20003
+ };
20004
+ const nodeTx = createNodeTx({
20005
+ sequence,
20006
+ txOut: output,
20007
+ parentOutPoint: input,
20008
+ includeAnchor: true
20009
+ });
20010
+ const directNodeTx = createNodeTx({
20011
+ sequence: directSequence ?? sequence + DIRECT_TIMELOCK_OFFSET,
20012
+ txOut: output,
20013
+ parentOutPoint: input,
20014
+ includeAnchor: false,
20015
+ applyFee: true
20016
+ });
20017
+ return { nodeTx, directNodeTx };
19786
20018
  }
19787
- function createLeafNodeTx(sequence, directSequence, parentOutPoint, txOut, shouldCalculateFee) {
19788
- const cpfpLeafTx = new import_btc_signer.Transaction({
19789
- version: 3,
19790
- allowUnknownOutputs: true
20019
+ function createRootNodeTx(parentTx, vout) {
20020
+ return createNodeTxs({
20021
+ parentTx,
20022
+ sequence: INITIAL_ROOT_NODE_SEQUENCE,
20023
+ vout
19791
20024
  });
19792
- cpfpLeafTx.addInput({
19793
- ...parentOutPoint,
19794
- sequence
20025
+ }
20026
+ function createZeroTimelockNodeTx(parentTx) {
20027
+ return createNodeTxs({
20028
+ parentTx,
20029
+ sequence: INITIAL_ROOT_NODE_SEQUENCE,
20030
+ directSequence: DIRECT_TIMELOCK_OFFSET,
20031
+ vout: 0
19795
20032
  });
19796
- cpfpLeafTx.addOutput(txOut);
19797
- cpfpLeafTx.addOutput(getEphemeralAnchorOutput());
19798
- const directLeafTx = new import_btc_signer.Transaction({
19799
- version: 3,
19800
- allowUnknownOutputs: true
20033
+ }
20034
+ function createInitialTimelockNodeTx(parentTx) {
20035
+ return createNodeTxs({
20036
+ parentTx,
20037
+ sequence: INITIAL_SEQUENCE,
20038
+ vout: 0
19801
20039
  });
19802
- directLeafTx.addInput({
19803
- ...parentOutPoint,
19804
- sequence: directSequence
20040
+ }
20041
+ function createDecrementedTimelockNodeTx(parentTx, currentTx) {
20042
+ const currentSequence = currentTx.getInput(0).sequence;
20043
+ if (!currentSequence) {
20044
+ throw new ValidationError("Current sequence not found", {
20045
+ field: "currentSequence",
20046
+ value: currentSequence
20047
+ });
20048
+ }
20049
+ return createNodeTxs({
20050
+ parentTx,
20051
+ sequence: getNextTransactionSequence(currentSequence).nextSequence,
20052
+ vout: 0
19805
20053
  });
19806
- const amountSats = txOut.amount ?? 0n;
19807
- let outputAmount = amountSats;
19808
- if (shouldCalculateFee) {
19809
- outputAmount = maybeApplyFee(amountSats);
20054
+ }
20055
+ function createTestUnilateralTimelockNodeTx(parentTx, nodeTx) {
20056
+ const sequence = nodeTx.getInput(0).sequence;
20057
+ if (!sequence) {
20058
+ throw new ValidationError("Sequence not found", {
20059
+ field: "sequence",
20060
+ value: sequence
20061
+ });
19810
20062
  }
19811
- directLeafTx.addOutput({
19812
- script: txOut.script,
19813
- amount: outputAmount
20063
+ const isBit30Defined = (sequence || 0) & 1 << 30;
20064
+ return createNodeTxs({
20065
+ parentTx,
20066
+ sequence: isBit30Defined | TEST_UNILATERAL_TIMELOCK,
20067
+ vout: 0
19814
20068
  });
19815
- return [cpfpLeafTx, directLeafTx];
19816
20069
  }
19817
20070
  function createRefundTx({
19818
20071
  sequence,
@@ -19868,110 +20121,109 @@ function getNextHTLCTransactionSequence(currSequence, isNodeTx) {
19868
20121
  };
19869
20122
  }
19870
20123
  function createRefundTxs({
19871
- sequence,
19872
- directSequence,
19873
- input,
19874
- directInput,
19875
- amountSats,
20124
+ nodeTx,
20125
+ directNodeTx,
19876
20126
  receivingPubkey,
19877
- network
20127
+ network,
20128
+ sequence
19878
20129
  }) {
19879
- const cpfpRefundTx = createRefundTx({
19880
- sequence,
19881
- input,
19882
- amountSats,
19883
- receivingPubkey,
19884
- network,
19885
- shouldCalculateFee: false,
19886
- includeAnchor: true
19887
- });
20130
+ const refundInput = {
20131
+ txid: (0, import_utils3.hexToBytes)(getTxId(nodeTx)),
20132
+ index: 0
20133
+ };
20134
+ const nodeAmountSats = nodeTx.getOutput(0).amount;
20135
+ if (nodeAmountSats === void 0) {
20136
+ throw new ValidationError("Node amount not found", {
20137
+ field: "nodeAmountSats",
20138
+ value: nodeAmountSats
20139
+ });
20140
+ }
19888
20141
  let directRefundTx;
19889
- let directFromCpfpRefundTx;
19890
- if (directSequence && directInput) {
20142
+ if (directNodeTx) {
20143
+ const directRefundInput = {
20144
+ txid: (0, import_utils3.hexToBytes)(getTxId(directNodeTx)),
20145
+ index: 0
20146
+ };
20147
+ const directAmountSats = directNodeTx.getOutput(0).amount;
20148
+ if (directAmountSats === void 0) {
20149
+ throw new ValidationError("Direct amount not found", {
20150
+ field: "directAmountSats",
20151
+ value: directAmountSats
20152
+ });
20153
+ }
19891
20154
  directRefundTx = createRefundTx({
19892
- sequence: directSequence,
19893
- input: directInput,
19894
- amountSats,
19895
- receivingPubkey,
19896
- network,
19897
- shouldCalculateFee: true,
19898
- includeAnchor: false
19899
- });
19900
- directFromCpfpRefundTx = createRefundTx({
19901
- sequence: directSequence,
19902
- input,
19903
- amountSats,
20155
+ sequence: sequence + DIRECT_TIMELOCK_OFFSET,
20156
+ input: directRefundInput,
20157
+ amountSats: directAmountSats,
19904
20158
  receivingPubkey,
19905
20159
  network,
19906
20160
  shouldCalculateFee: true,
19907
20161
  includeAnchor: false
19908
20162
  });
19909
- } else if (directInput && !directSequence) {
19910
- throw new ValidationError(
19911
- "directSequence must be provided if directInput is",
19912
- {
19913
- field: "directSequence",
19914
- value: directSequence
19915
- }
19916
- );
19917
20163
  }
19918
- return { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx };
19919
- }
19920
- function createConnectorRefundTransactions(sequence, cpfpNodeOutPoint, directNodeOutPoint, connectorOutput, amountSats, receiverPubKey, network, shouldCalculateFee) {
19921
- const cpfpRefundTx = new import_btc_signer.Transaction({
19922
- version: 3,
19923
- allowUnknownOutputs: true
20164
+ const cpfpRefundTx = createRefundTx({
20165
+ sequence,
20166
+ input: refundInput,
20167
+ amountSats: nodeAmountSats,
20168
+ receivingPubkey,
20169
+ network,
20170
+ shouldCalculateFee: false,
20171
+ includeAnchor: true
19924
20172
  });
19925
- cpfpRefundTx.addInput({
19926
- ...cpfpNodeOutPoint,
19927
- sequence
20173
+ const directFromCpfpRefundTx = createRefundTx({
20174
+ sequence: sequence + DIRECT_TIMELOCK_OFFSET,
20175
+ input: refundInput,
20176
+ amountSats: nodeAmountSats,
20177
+ receivingPubkey,
20178
+ network,
20179
+ shouldCalculateFee: true,
20180
+ includeAnchor: false
19928
20181
  });
19929
- cpfpRefundTx.addInput(connectorOutput);
19930
- const receiverScript = getP2TRScriptFromPublicKey(receiverPubKey, network);
19931
- cpfpRefundTx.addOutput({
19932
- script: receiverScript,
19933
- amount: amountSats
20182
+ return { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx };
20183
+ }
20184
+ function createInitialTimelockRefundTxs(params) {
20185
+ return createRefundTxs({
20186
+ ...params,
20187
+ sequence: INITIAL_SEQUENCE
19934
20188
  });
19935
- const directRefundTx = new import_btc_signer.Transaction({
19936
- version: 3,
19937
- allowUnknownOutputs: true
20189
+ }
20190
+ function createDecrementedTimelockRefundTxs(params) {
20191
+ const nextSequence = getNextTransactionSequence(params.sequence).nextSequence;
20192
+ return createRefundTxs({
20193
+ ...params,
20194
+ sequence: nextSequence
19938
20195
  });
19939
- directRefundTx.addInput({
19940
- ...directNodeOutPoint,
19941
- sequence
20196
+ }
20197
+ function createCurrentTimelockRefundTxs(params) {
20198
+ return createRefundTxs(params);
20199
+ }
20200
+ function createTestUnilateralRefundTxs(params) {
20201
+ return createRefundTxs({
20202
+ ...params,
20203
+ sequence: TEST_UNILATERAL_SEQUENCE
19942
20204
  });
19943
- directRefundTx.addInput(connectorOutput);
19944
- let outputAmount = amountSats;
19945
- if (shouldCalculateFee) {
19946
- outputAmount = maybeApplyFee(amountSats);
20205
+ }
20206
+ function createConnectorRefundTxs(params) {
20207
+ const { connectorOutput, ...baseParams } = params;
20208
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createDecrementedTimelockRefundTxs(baseParams);
20209
+ cpfpRefundTx.addInput(connectorOutput);
20210
+ if (directRefundTx) {
20211
+ directRefundTx.addInput(connectorOutput);
19947
20212
  }
19948
- directRefundTx.addOutput({
19949
- script: receiverScript,
19950
- amount: outputAmount
19951
- });
19952
- const directFromCpfpTx = new import_btc_signer.Transaction({
19953
- version: 3,
19954
- allowUnknownOutputs: true
19955
- });
19956
- directFromCpfpTx.addInput({
19957
- ...cpfpNodeOutPoint,
19958
- sequence
19959
- });
19960
- directFromCpfpTx.addInput(connectorOutput);
19961
- directFromCpfpTx.addOutput({
19962
- script: receiverScript,
19963
- amount: outputAmount
19964
- });
19965
- return [cpfpRefundTx, directRefundTx, directFromCpfpTx];
20213
+ if (directFromCpfpRefundTx) {
20214
+ directFromCpfpRefundTx.addInput(connectorOutput);
20215
+ }
20216
+ return { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx };
19966
20217
  }
19967
20218
  function getCurrentTimelock(currSequence) {
19968
20219
  return (currSequence || 0) & 65535;
19969
20220
  }
19970
20221
  function getTransactionSequence(currSequence) {
19971
20222
  const timelock = getCurrentTimelock(currSequence);
20223
+ const isBit30Defined = (currSequence || 0) & 1 << 30;
19972
20224
  return {
19973
- nextSequence: 1 << 30 | timelock,
19974
- nextDirectSequence: 1 << 30 | timelock + DIRECT_TIMELOCK_OFFSET
20225
+ nextSequence: isBit30Defined | timelock,
20226
+ nextDirectSequence: isBit30Defined | timelock + DIRECT_TIMELOCK_OFFSET
19975
20227
  };
19976
20228
  }
19977
20229
  function checkIfValidSequence(currSequence) {
@@ -19990,6 +20242,13 @@ function checkIfValidSequence(currSequence) {
19990
20242
  });
19991
20243
  }
19992
20244
  }
20245
+ function isZeroTimelock(currSequence) {
20246
+ return getCurrentTimelock(currSequence) === 0;
20247
+ }
20248
+ function doesTxnNeedRenewed(currSequence) {
20249
+ const currentTimelock = getCurrentTimelock(currSequence);
20250
+ return currentTimelock <= 100;
20251
+ }
19993
20252
  function doesLeafNeedRefresh(currSequence, isNodeTx) {
19994
20253
  const currentTimelock = getCurrentTimelock(currSequence);
19995
20254
  if (isNodeTx) {
@@ -20000,6 +20259,7 @@ function doesLeafNeedRefresh(currSequence, isNodeTx) {
20000
20259
  function getNextTransactionSequence(currSequence, isNodeTx) {
20001
20260
  const currentTimelock = getCurrentTimelock(currSequence);
20002
20261
  const nextTimelock = currentTimelock - TIME_LOCK_INTERVAL;
20262
+ const isBit30Defined = (currSequence || 0) & 1 << 30;
20003
20263
  if (isNodeTx && nextTimelock < 0) {
20004
20264
  throw new ValidationError("timelock interval is less than 0", {
20005
20265
  field: "nextTimelock",
@@ -20014,8 +20274,8 @@ function getNextTransactionSequence(currSequence, isNodeTx) {
20014
20274
  });
20015
20275
  }
20016
20276
  return {
20017
- nextSequence: 1 << 30 | nextTimelock,
20018
- nextDirectSequence: 1 << 30 | nextTimelock + DIRECT_TIMELOCK_OFFSET
20277
+ nextSequence: isBit30Defined | nextTimelock,
20278
+ nextDirectSequence: isBit30Defined | nextTimelock + DIRECT_TIMELOCK_OFFSET
20019
20279
  };
20020
20280
  }
20021
20281
  function getEphemeralAnchorOutput() {
@@ -20028,14 +20288,13 @@ function getEphemeralAnchorOutput() {
20028
20288
 
20029
20289
  // src/services/transfer.ts
20030
20290
  var import_secp256k12 = require("@noble/curves/secp256k1");
20031
- var import_utils4 = require("@noble/curves/utils");
20291
+ var import_utils5 = require("@noble/curves/utils");
20032
20292
  var import_sha24 = require("@noble/hashes/sha2");
20033
- var import_btc_signer2 = require("@scure/btc-signer");
20034
20293
  var ecies = __toESM(require("eciesjs"), 1);
20035
20294
  var import_uuidv7 = require("uuidv7");
20036
20295
 
20037
20296
  // src/utils/transfer_package.ts
20038
- var import_utils3 = require("@noble/curves/utils");
20297
+ var import_utils4 = require("@noble/curves/utils");
20039
20298
  var import_sha23 = require("@noble/hashes/sha2");
20040
20299
  function getTransferPackageSigningPayload(transferID, transferPackage) {
20041
20300
  const encryptedPayload = transferPackage.keyTweakPackage;
@@ -20044,7 +20303,7 @@ function getTransferPackageSigningPayload(transferID, transferPackage) {
20044
20303
  ).map(([key, value]) => ({ key, value }));
20045
20304
  pairs.sort((a, b) => a.key.localeCompare(b.key));
20046
20305
  const encoder = new TextEncoder();
20047
- let message = (0, import_utils3.hexToBytes)(transferID.replaceAll("-", ""));
20306
+ let message = (0, import_utils4.hexToBytes)(transferID.replaceAll("-", ""));
20048
20307
  for (const pair of pairs) {
20049
20308
  const keyPart = encoder.encode(pair.key + ":");
20050
20309
  const separator = encoder.encode(";");
@@ -20059,13 +20318,6 @@ function getTransferPackageSigningPayload(transferID, transferPackage) {
20059
20318
  }
20060
20319
 
20061
20320
  // src/services/transfer.ts
20062
- function getSigningJobProto(signingJob) {
20063
- return {
20064
- signingPublicKey: signingJob.signingPublicKey,
20065
- rawTx: signingJob.rawTx,
20066
- signingNonceCommitment: signingJob.signingNonceCommitment.commitment
20067
- };
20068
- }
20069
20321
  var BaseTransferService = class {
20070
20322
  config;
20071
20323
  connectionManager;
@@ -20412,7 +20664,7 @@ var BaseTransferService = class {
20412
20664
  }
20413
20665
  async prepareSendTransferKeyTweaks(transferID, receiverIdentityPubkey, leaves, cpfpRefundSignatureMap, directRefundSignatureMap, directFromCpfpRefundSignatureMap) {
20414
20666
  const receiverEciesPubKey = ecies.PublicKey.fromHex(
20415
- (0, import_utils4.bytesToHex)(receiverIdentityPubkey)
20667
+ (0, import_utils5.bytesToHex)(receiverIdentityPubkey)
20416
20668
  );
20417
20669
  const leavesTweaksMap = /* @__PURE__ */ new Map();
20418
20670
  for (const leaf of leaves) {
@@ -20453,7 +20705,7 @@ var BaseTransferService = class {
20453
20705
  throw new Error(`Share not found for operator ${operator.id}`);
20454
20706
  }
20455
20707
  const pubkeyTweak = import_secp256k12.secp256k1.getPublicKey(
20456
- (0, import_utils4.numberToBytesBE)(share.share, 32),
20708
+ (0, import_utils5.numberToBytesBE)(share.share, 32),
20457
20709
  true
20458
20710
  );
20459
20711
  pubkeySharesTweak.set(identifier, pubkeyTweak);
@@ -20478,7 +20730,7 @@ var BaseTransferService = class {
20478
20730
  leafTweaksMap.set(identifier, {
20479
20731
  leafId: leaf.leaf.id,
20480
20732
  secretShareTweak: {
20481
- secretShare: (0, import_utils4.numberToBytesBE)(share.share, 32),
20733
+ secretShare: (0, import_utils5.numberToBytesBE)(share.share, 32),
20482
20734
  proofs: share.proofs
20483
20735
  },
20484
20736
  pubkeySharesTweak: Object.fromEntries(pubkeySharesTweak),
@@ -20501,7 +20753,7 @@ var BaseTransferService = class {
20501
20753
  return void 0;
20502
20754
  }
20503
20755
  compareTransfers(transfer1, transfer2) {
20504
- return transfer1.id === transfer2.id && (0, import_utils4.equalBytes)(
20756
+ return transfer1.id === transfer2.id && (0, import_utils5.equalBytes)(
20505
20757
  transfer1.senderIdentityPublicKey,
20506
20758
  transfer2.senderIdentityPublicKey
20507
20759
  ) && transfer1.status === transfer2.status && transfer1.totalValue === transfer2.totalValue && transfer1.expiryTime?.getTime() === transfer2.expiryTime?.getTime() && transfer1.leaves.length === transfer2.leaves.length;
@@ -20747,42 +20999,27 @@ var TransferService = class extends BaseTransferService {
20747
20999
  throw new Error(`Leaf data not found for leaf ${leaf.leaf.id}`);
20748
21000
  }
20749
21001
  const nodeTx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
20750
- const cpfpNodeOutPoint = {
20751
- txid: (0, import_utils4.hexToBytes)(getTxId(nodeTx)),
20752
- index: 0
20753
- };
20754
21002
  let directNodeTx;
20755
- let directNodeOutPoint;
20756
21003
  if (leaf.leaf.directTx.length > 0) {
20757
21004
  directNodeTx = getTxFromRawTxBytes(leaf.leaf.directTx);
20758
- directNodeOutPoint = {
20759
- txid: (0, import_utils4.hexToBytes)(getTxId(directNodeTx)),
20760
- index: 0
20761
- };
20762
21005
  }
20763
21006
  const currRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
20764
- const sequence = currRefundTx.getInput(0).sequence;
20765
- if (!sequence) {
21007
+ const currentSequence = currRefundTx.getInput(0).sequence;
21008
+ if (!currentSequence) {
20766
21009
  throw new ValidationError("Invalid refund transaction", {
20767
21010
  field: "sequence",
20768
21011
  value: currRefundTx.getInput(0),
20769
21012
  expected: "Non-null sequence"
20770
21013
  });
20771
21014
  }
20772
- const { nextSequence, nextDirectSequence } = isForClaim ? getTransactionSequence(sequence) : getNextTransactionSequence(sequence);
20773
- const amountSats = currRefundTx.getOutput(0).amount;
20774
- if (amountSats === void 0) {
20775
- throw new Error("Amount not found in signRefunds");
20776
- }
20777
- const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
20778
- sequence: nextSequence,
20779
- directSequence: nextDirectSequence,
20780
- input: cpfpNodeOutPoint,
20781
- directInput: directNodeOutPoint,
20782
- amountSats,
21015
+ const refundTxsParams = {
21016
+ nodeTx,
21017
+ directNodeTx,
21018
+ sequence: currentSequence,
20783
21019
  receivingPubkey: refundSigningData.receivingPubkey,
20784
21020
  network: this.config.getNetwork()
20785
- });
21021
+ };
21022
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = isForClaim ? createCurrentTimelockRefundTxs(refundTxsParams) : createDecrementedTimelockRefundTxs(refundTxsParams);
20786
21023
  refundSigningData.refundTx = cpfpRefundTx;
20787
21024
  refundSigningData.directRefundTx = directRefundTx;
20788
21025
  refundSigningData.directFromCpfpRefundTx = directFromCpfpRefundTx;
@@ -20890,7 +21127,7 @@ var TransferService = class extends BaseTransferService {
20890
21127
  throw new Error(`Share not found for operator ${operator.id}`);
20891
21128
  }
20892
21129
  const pubkeyTweak = import_secp256k12.secp256k1.getPublicKey(
20893
- (0, import_utils4.numberToBytesBE)(share.share, 32)
21130
+ (0, import_utils5.numberToBytesBE)(share.share, 32)
20894
21131
  );
20895
21132
  pubkeySharesTweak.set(identifier, pubkeyTweak);
20896
21133
  }
@@ -20903,7 +21140,7 @@ var TransferService = class extends BaseTransferService {
20903
21140
  leafTweaksMap.set(identifier, {
20904
21141
  leafId: leaf.leaf.id,
20905
21142
  secretShareTweak: {
20906
- secretShare: (0, import_utils4.numberToBytesBE)(share.share, 32),
21143
+ secretShare: (0, import_utils5.numberToBytesBE)(share.share, 32),
20907
21144
  proofs: share.proofs
20908
21145
  },
20909
21146
  pubkeySharesTweak: Object.fromEntries(pubkeySharesTweak)
@@ -20995,71 +21232,127 @@ var TransferService = class extends BaseTransferService {
20995
21232
  throw new Error(`Error querying pending transfers by sender: ${error}`);
20996
21233
  }
20997
21234
  }
20998
- async refreshTimelockNodesInternal(node, parentNode, useTestUnilateralSequence) {
20999
- const signingJobs = [];
21000
- const parentNodeTx = getTxFromRawTxBytes(parentNode.nodeTx);
21001
- const parentNodeOutput = parentNodeTx.getOutput(0);
21002
- if (!parentNodeOutput) {
21003
- throw Error("Could not get parent node output");
21235
+ async renewRefundTxn(node, parentNode) {
21236
+ const sparkClient = await this.connectionManager.createSparkClient(
21237
+ this.config.getCoordinatorAddress()
21238
+ );
21239
+ const signingJobs = await this.createRenewRefundSigningJobs(
21240
+ node,
21241
+ parentNode
21242
+ );
21243
+ const statechainCommitments = await sparkClient.get_signing_commitments({
21244
+ nodeIds: [node.id],
21245
+ count: signingJobs.length
21246
+ });
21247
+ const mappedSigningJobs = signingJobs.map((signingJob, index) => {
21248
+ const signingNonceCommitments = statechainCommitments.signingCommitments[index]?.signingNonceCommitments;
21249
+ if (!signingNonceCommitments) {
21250
+ throw new Error("Signing nonce commitments not found");
21251
+ }
21252
+ return {
21253
+ ...signingJob,
21254
+ signingNonceCommitments
21255
+ };
21256
+ });
21257
+ const userSignedTxSigningJobs = await this.signingService.signSigningJobs(mappedSigningJobs);
21258
+ const renewRefundTimelockSigningJob = {
21259
+ nodeTxSigningJob: userSignedTxSigningJobs.get("node"),
21260
+ refundTxSigningJob: userSignedTxSigningJobs.get("cpfp"),
21261
+ directNodeTxSigningJob: userSignedTxSigningJobs.get("directNode"),
21262
+ directRefundTxSigningJob: userSignedTxSigningJobs.get("direct"),
21263
+ directFromCpfpRefundTxSigningJob: userSignedTxSigningJobs.get("directFromCpfp")
21264
+ };
21265
+ const response = await sparkClient.renew_leaf({
21266
+ leafId: node.id,
21267
+ signingJobs: {
21268
+ $case: "renewRefundTimelockSigningJob",
21269
+ renewRefundTimelockSigningJob
21270
+ }
21271
+ });
21272
+ if (response.renewResult?.$case !== "renewRefundTimelockResult" || !response.renewResult?.renewRefundTimelockResult.node) {
21273
+ throw new ValidationError("Unexpected renew result", {
21274
+ field: "renewResult",
21275
+ value: response.renewResult
21276
+ });
21004
21277
  }
21005
- const nodeTx = getTxFromRawTxBytes(node.nodeTx);
21006
- const nodeInput = nodeTx.getInput(0);
21007
- const nodeOutput = nodeTx.getOutput(0);
21008
- if (!nodeOutput) {
21009
- throw Error("Could not get node output");
21010
- }
21011
- let directNodeTx;
21012
- let directNodeInput;
21013
- if (node.directTx.length > 0) {
21014
- directNodeTx = getTxFromRawTxBytes(node.directTx);
21015
- directNodeInput = directNodeTx.getInput(0);
21016
- }
21017
- const currSequence = nodeInput.sequence;
21018
- if (!currSequence) {
21019
- throw new ValidationError("Invalid node transaction", {
21020
- field: "sequence",
21021
- value: nodeInput,
21022
- expected: "Non-null sequence"
21278
+ return response.renewResult?.renewRefundTimelockResult.node;
21279
+ }
21280
+ async renewNodeTxn(node, parentNode) {
21281
+ const sparkClient = await this.connectionManager.createSparkClient(
21282
+ this.config.getCoordinatorAddress()
21283
+ );
21284
+ const signingJobs = await this.createRenewNodeSigningJobs(node, parentNode);
21285
+ const statechainCommitments = await sparkClient.get_signing_commitments({
21286
+ nodeIds: [node.id],
21287
+ count: signingJobs.length
21288
+ });
21289
+ const mappedSigningJobs = signingJobs.map((signingJob, index) => {
21290
+ const signingNonceCommitments = statechainCommitments.signingCommitments[index]?.signingNonceCommitments;
21291
+ if (!signingNonceCommitments) {
21292
+ throw new Error("Signing nonce commitments not found");
21293
+ }
21294
+ return {
21295
+ ...signingJob,
21296
+ signingNonceCommitments
21297
+ };
21298
+ });
21299
+ const userSignedTxSigningJobs = await this.signingService.signSigningJobs(mappedSigningJobs);
21300
+ const response = await sparkClient.renew_leaf({
21301
+ leafId: node.id,
21302
+ signingJobs: {
21303
+ $case: "renewNodeTimelockSigningJob",
21304
+ renewNodeTimelockSigningJob: {
21305
+ splitNodeTxSigningJob: userSignedTxSigningJobs.get("split"),
21306
+ splitNodeDirectTxSigningJob: userSignedTxSigningJobs.get("directSplit"),
21307
+ nodeTxSigningJob: userSignedTxSigningJobs.get("node"),
21308
+ directNodeTxSigningJob: userSignedTxSigningJobs.get("directNode"),
21309
+ refundTxSigningJob: userSignedTxSigningJobs.get("cpfp"),
21310
+ directRefundTxSigningJob: userSignedTxSigningJobs.get("direct"),
21311
+ directFromCpfpRefundTxSigningJob: userSignedTxSigningJobs.get("directFromCpfp")
21312
+ }
21313
+ }
21314
+ });
21315
+ if (response.renewResult?.$case !== "renewNodeTimelockResult" || !response.renewResult?.renewNodeTimelockResult.node) {
21316
+ throw new ValidationError("Unexpected renew result", {
21317
+ field: "renewResult",
21318
+ value: response.renewResult
21023
21319
  });
21024
21320
  }
21025
- let { nextSequence, nextDirectSequence } = getNextTransactionSequence(
21026
- currSequence,
21027
- true
21321
+ return response.renewResult.renewNodeTimelockResult.node;
21322
+ }
21323
+ async createRenewRefundSigningJobs(node, parentNode) {
21324
+ const signingJobs = [];
21325
+ const parentTx = getTxFromRawTxBytes(parentNode.nodeTx);
21326
+ const parentNodeOutput = getTxFromRawTxBytes(parentNode.nodeTx).getOutput(
21327
+ 0
21028
21328
  );
21029
- const output = {
21329
+ if (!parentNodeOutput) {
21330
+ throw new Error("Parent node output not found");
21331
+ }
21332
+ const unsignedParentNodeOutput = {
21030
21333
  script: parentNodeOutput.script,
21031
21334
  amount: parentNodeOutput.amount
21032
21335
  };
21033
- const newNodeInput = {
21034
- txid: nodeInput.txid,
21035
- index: nodeInput.index,
21036
- sequence: useTestUnilateralSequence ? TEST_UNILATERAL_SEQUENCE : nextSequence
21037
- };
21038
- const newDirectInput = directNodeTx && directNodeInput ? {
21039
- txid: directNodeInput.txid,
21040
- index: directNodeInput.index,
21041
- sequence: useTestUnilateralSequence ? TEST_UNILATERAL_DIRECT_SEQUENCE : nextDirectSequence
21042
- } : void 0;
21043
- const { cpfpNodeTx, directNodeTx: newDirectNodeTx } = createNodeTxs(
21044
- output,
21045
- newNodeInput,
21046
- newDirectInput
21047
- );
21048
- const newCpfpNodeOutput = cpfpNodeTx.getOutput(0);
21049
- if (!newCpfpNodeOutput) {
21050
- throw Error("Could not get new cpfp node output");
21051
- }
21052
- const newDirectNodeOutput = newDirectNodeTx?.getOutput(0);
21053
- const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation({
21336
+ const keyDerivation = {
21054
21337
  type: "leaf" /* LEAF */,
21055
21338
  path: node.id
21056
- });
21339
+ };
21340
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
21341
+ const nodeTx = getTxFromRawTxBytes(node.nodeTx);
21342
+ const refundTx = getTxFromRawTxBytes(node.refundTx);
21343
+ const { nodeTx: newNodeTx, directNodeTx: newDirectNodeTx } = createDecrementedTimelockNodeTx(parentTx, nodeTx);
21057
21344
  signingJobs.push({
21058
21345
  signingPublicKey,
21059
- rawTx: cpfpNodeTx.toBytes(),
21346
+ rawTx: newNodeTx.toBytes(),
21060
21347
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
21061
21348
  type: "node",
21062
- parentTxOut: parentNodeOutput
21349
+ parentTxOut: unsignedParentNodeOutput,
21350
+ leafId: node.id,
21351
+ keyDerivation: {
21352
+ type: "leaf" /* LEAF */,
21353
+ path: node.id
21354
+ },
21355
+ verifyingKey: node.verifyingPublicKey
21063
21356
  });
21064
21357
  if (newDirectNodeTx) {
21065
21358
  signingJobs.push({
@@ -21067,537 +21360,299 @@ var TransferService = class extends BaseTransferService {
21067
21360
  rawTx: newDirectNodeTx.toBytes(),
21068
21361
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
21069
21362
  type: "directNode",
21070
- parentTxOut: parentNodeOutput
21363
+ parentTxOut: unsignedParentNodeOutput,
21364
+ leafId: node.id,
21365
+ keyDerivation: {
21366
+ type: "leaf" /* LEAF */,
21367
+ path: node.id
21368
+ },
21369
+ verifyingKey: node.verifyingPublicKey
21071
21370
  });
21072
21371
  }
21073
- const newCpfpRefundOutPoint = {
21074
- txid: (0, import_utils4.hexToBytes)(getTxId(cpfpNodeTx)),
21075
- index: 0
21076
- };
21077
- let newDirectRefundOutPoint;
21078
- if (newDirectNodeTx) {
21079
- newDirectRefundOutPoint = {
21080
- txid: (0, import_utils4.hexToBytes)(getTxId(newDirectNodeTx)),
21081
- index: 0
21082
- };
21372
+ const newCpfpNodeOutput = newNodeTx.getOutput(0);
21373
+ if (!newCpfpNodeOutput) {
21374
+ throw Error("Could not get new cpfp node output");
21083
21375
  }
21084
- const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
21085
- sequence: INITIAL_SEQUENCE,
21086
- directSequence: INITIAL_DIRECT_SEQUENCE,
21087
- input: newCpfpRefundOutPoint,
21088
- directInput: newDirectRefundOutPoint,
21089
- amountSats: nodeOutput.amount,
21090
- receivingPubkey: await this.config.signer.getPublicKeyFromDerivation({
21091
- type: "leaf" /* LEAF */,
21092
- path: node.id
21093
- }),
21376
+ const newDirectNodeOutput = newDirectNodeTx?.getOutput(0);
21377
+ const amountSats = refundTx.getOutput(0).amount;
21378
+ if (amountSats === void 0) {
21379
+ throw new Error("Amount not found in extendTimelock");
21380
+ }
21381
+ const directAmountSats = newDirectNodeOutput?.amount;
21382
+ if (directAmountSats === void 0) {
21383
+ throw new Error("Amount not found in extendTimelock");
21384
+ }
21385
+ const {
21386
+ cpfpRefundTx: newRefundTx,
21387
+ directRefundTx: newDirectRefundTx,
21388
+ directFromCpfpRefundTx: newDirectFromCpfpRefundTx
21389
+ } = createInitialTimelockRefundTxs({
21390
+ nodeTx: newNodeTx,
21391
+ directNodeTx: newDirectNodeTx,
21392
+ receivingPubkey: signingPublicKey,
21094
21393
  network: this.config.getNetwork()
21095
21394
  });
21096
21395
  signingJobs.push({
21097
21396
  signingPublicKey,
21098
- rawTx: cpfpRefundTx.toBytes(),
21397
+ rawTx: newRefundTx.toBytes(),
21099
21398
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
21100
21399
  type: "cpfp",
21101
- parentTxOut: newCpfpNodeOutput
21400
+ parentTxOut: newCpfpNodeOutput,
21401
+ leafId: node.id,
21402
+ keyDerivation,
21403
+ verifyingKey: node.verifyingPublicKey
21102
21404
  });
21103
- if (directRefundTx && newDirectNodeOutput) {
21405
+ if (newDirectRefundTx && newDirectNodeOutput) {
21104
21406
  signingJobs.push({
21105
21407
  signingPublicKey,
21106
- rawTx: directRefundTx.toBytes(),
21408
+ rawTx: newDirectRefundTx.toBytes(),
21107
21409
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
21108
21410
  type: "direct",
21109
- parentTxOut: newDirectNodeOutput
21411
+ parentTxOut: newDirectNodeOutput,
21412
+ leafId: node.id,
21413
+ keyDerivation,
21414
+ verifyingKey: node.verifyingPublicKey
21110
21415
  });
21111
21416
  }
21112
- if (directFromCpfpRefundTx && newCpfpNodeOutput) {
21417
+ if (newDirectFromCpfpRefundTx && newDirectNodeOutput) {
21113
21418
  signingJobs.push({
21114
21419
  signingPublicKey,
21115
- rawTx: directFromCpfpRefundTx.toBytes(),
21420
+ rawTx: newDirectFromCpfpRefundTx.toBytes(),
21116
21421
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
21117
21422
  type: "directFromCpfp",
21118
- parentTxOut: newCpfpNodeOutput
21423
+ parentTxOut: newCpfpNodeOutput,
21424
+ leafId: node.id,
21425
+ keyDerivation,
21426
+ verifyingKey: node.verifyingPublicKey
21119
21427
  });
21120
21428
  }
21121
- const sparkClient = await this.connectionManager.createSparkClient(
21122
- this.config.getCoordinatorAddress()
21123
- );
21124
- const response = await sparkClient.refresh_timelock_v2({
21125
- leafId: node.id,
21126
- ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
21127
- signingJobs: signingJobs.map(getSigningJobProto)
21128
- });
21129
- if (signingJobs.length !== response.signingResults.length) {
21130
- throw Error(
21131
- `number of signing jobs and signing results do not match: ${signingJobs.length} !== ${response.signingResults.length}`
21132
- );
21133
- }
21134
- let nodeSignatures = [];
21135
- let leafCpfpSignature;
21136
- let leafDirectSignature;
21137
- let cpfpRefundSignature;
21138
- let directRefundSignature;
21139
- let directFromCpfpRefundSignature;
21140
- for (const [i, signingResult] of response.signingResults.entries()) {
21141
- const signingJob = signingJobs[i];
21142
- if (!signingJob || !signingResult) {
21143
- throw Error("Signing job does not exist");
21144
- }
21145
- const rawTx = getTxFromRawTxBytes(signingJob.rawTx);
21146
- const txOut = signingJob.parentTxOut;
21147
- if (!txOut) {
21148
- throw Error("Could not get tx out");
21149
- }
21150
- const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
21151
- const userSignature = await this.config.signer.signFrost({
21152
- message: rawTxSighash,
21153
- keyDerivation: {
21154
- type: "leaf" /* LEAF */,
21155
- path: node.id
21156
- },
21157
- publicKey: signingJob.signingPublicKey,
21158
- verifyingKey: signingResult.verifyingKey,
21159
- selfCommitment: signingJob.signingNonceCommitment,
21160
- statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
21161
- adaptorPubKey: new Uint8Array()
21162
- });
21163
- const signature = await this.config.signer.aggregateFrost({
21164
- message: rawTxSighash,
21165
- statechainSignatures: signingResult.signingResult?.signatureShares,
21166
- statechainPublicKeys: signingResult.signingResult?.publicKeys,
21167
- verifyingKey: signingResult.verifyingKey,
21168
- statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
21169
- selfCommitment: signingJob.signingNonceCommitment,
21170
- publicKey: signingJob.signingPublicKey,
21171
- selfSignature: userSignature,
21172
- adaptorPubKey: new Uint8Array()
21173
- });
21174
- if (signingJob.type === "node") {
21175
- leafCpfpSignature = signature;
21176
- } else if (signingJob.type === "directNode") {
21177
- leafDirectSignature = signature;
21178
- } else if (signingJob.type === "cpfp") {
21179
- cpfpRefundSignature = signature;
21180
- } else if (signingJob.type === "direct") {
21181
- directRefundSignature = signature;
21182
- } else if (signingJob.type === "directFromCpfp") {
21183
- directFromCpfpRefundSignature = signature;
21184
- }
21185
- }
21186
- nodeSignatures.push({
21187
- nodeId: node.id,
21188
- nodeTxSignature: leafCpfpSignature || new Uint8Array(),
21189
- directNodeTxSignature: leafDirectSignature || new Uint8Array(),
21190
- refundTxSignature: cpfpRefundSignature || new Uint8Array(),
21191
- directRefundTxSignature: directRefundSignature || new Uint8Array(),
21192
- directFromCpfpRefundTxSignature: directFromCpfpRefundSignature || new Uint8Array()
21193
- });
21194
- const result = await sparkClient.finalize_node_signatures_v2({
21195
- intent: 3 /* REFRESH */,
21196
- nodeSignatures
21197
- });
21198
- return result;
21199
- }
21200
- async refreshTimelockNodes(node, parentNode) {
21201
- return await this.refreshTimelockNodesInternal(node, parentNode);
21429
+ return signingJobs;
21202
21430
  }
21203
- async extendTimelock(node) {
21204
- const nodeTx = getTxFromRawTxBytes(node.nodeTx);
21205
- const refundTx = getTxFromRawTxBytes(node.refundTx);
21206
- const refundSequence = refundTx.getInput(0).sequence || 0;
21207
- const newNodeOutPoint = {
21208
- txid: (0, import_utils4.hexToBytes)(getTxId(nodeTx)),
21209
- index: 0
21210
- };
21211
- const {
21212
- nextSequence: newNodeSequence,
21213
- nextDirectSequence: newDirectNodeSequence
21214
- } = getNextTransactionSequence(refundSequence);
21215
- const newNodeTx = new import_btc_signer2.Transaction({
21216
- version: 3,
21217
- allowUnknownOutputs: true
21218
- });
21219
- newNodeTx.addInput({ ...newNodeOutPoint, sequence: newNodeSequence });
21220
- const originalOutput = nodeTx.getOutput(0);
21221
- if (!originalOutput) {
21222
- throw Error("Could not get original node output");
21223
- }
21224
- newNodeTx.addOutput({
21225
- script: originalOutput.script,
21226
- amount: originalOutput.amount
21227
- });
21228
- newNodeTx.addOutput(getEphemeralAnchorOutput());
21229
- let newDirectNodeTx;
21230
- if (node.directTx.length > 0) {
21231
- newDirectNodeTx = new import_btc_signer2.Transaction({
21232
- version: 3,
21233
- allowUnknownOutputs: true
21234
- });
21235
- newDirectNodeTx.addInput({
21236
- ...newNodeOutPoint,
21237
- sequence: newDirectNodeSequence
21238
- });
21239
- newDirectNodeTx.addOutput({
21240
- script: originalOutput.script,
21241
- amount: maybeApplyFee(originalOutput.amount)
21242
- });
21243
- }
21244
- const newCpfpRefundOutPoint = {
21245
- txid: (0, import_utils4.hexToBytes)(getTxId(newNodeTx)),
21246
- index: 0
21247
- };
21248
- let newDirectRefundOutPoint;
21249
- if (newDirectNodeTx) {
21250
- newDirectRefundOutPoint = {
21251
- txid: (0, import_utils4.hexToBytes)(getTxId(newDirectNodeTx)),
21252
- index: 0
21253
- };
21254
- }
21255
- const amountSats = refundTx.getOutput(0).amount;
21256
- if (amountSats === void 0) {
21257
- throw new Error("Amount not found in extendTimelock");
21258
- }
21259
- const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation({
21260
- type: "leaf" /* LEAF */,
21261
- path: node.id
21262
- });
21263
- const {
21264
- cpfpRefundTx: newCpfpRefundTx,
21265
- directRefundTx: newDirectRefundTx,
21266
- directFromCpfpRefundTx: newDirectFromCpfpRefundTx
21267
- } = createRefundTxs({
21268
- sequence: INITIAL_SEQUENCE,
21269
- directSequence: INITIAL_DIRECT_SEQUENCE,
21270
- input: newCpfpRefundOutPoint,
21271
- directInput: newDirectRefundOutPoint,
21272
- amountSats,
21273
- receivingPubkey: signingPublicKey,
21274
- network: this.config.getNetwork()
21275
- });
21276
- if (!newCpfpRefundTx) {
21277
- throw new ValidationError(
21278
- "Failed to create refund transactions in extendTimelock"
21279
- );
21280
- }
21281
- const nodeSighash = getSigHashFromTx(newNodeTx, 0, nodeTx.getOutput(0));
21282
- const directNodeSighash = newDirectNodeTx ? getSigHashFromTx(newDirectNodeTx, 0, nodeTx.getOutput(0)) : void 0;
21283
- const cpfpRefundSighash = getSigHashFromTx(
21284
- newCpfpRefundTx,
21285
- 0,
21286
- newNodeTx.getOutput(0)
21431
+ async createRenewNodeSigningJobs(node, parentNode) {
21432
+ const signingJobs = [];
21433
+ const parentTx = getTxFromRawTxBytes(parentNode.nodeTx);
21434
+ const parentNodeOutput = getTxFromRawTxBytes(parentNode.nodeTx).getOutput(
21435
+ 0
21287
21436
  );
21288
- const directRefundSighash = newDirectNodeTx && newDirectRefundTx ? getSigHashFromTx(newDirectRefundTx, 0, newDirectNodeTx.getOutput(0)) : void 0;
21289
- const directFromCpfpRefundSighash = newDirectFromCpfpRefundTx ? getSigHashFromTx(newDirectFromCpfpRefundTx, 0, newNodeTx.getOutput(0)) : void 0;
21290
- const newNodeSigningJob = {
21291
- signingPublicKey,
21292
- rawTx: newNodeTx.toBytes(),
21293
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
21294
- };
21295
- const newDirectNodeSigningJob = newDirectNodeTx ? {
21296
- signingPublicKey,
21297
- rawTx: newDirectNodeTx.toBytes(),
21298
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
21299
- } : void 0;
21300
- const newCpfpRefundSigningJob = {
21301
- signingPublicKey,
21302
- rawTx: newCpfpRefundTx.toBytes(),
21303
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
21437
+ const unsignedParentNodeOutput = {
21438
+ script: parentNodeOutput.script,
21439
+ amount: parentNodeOutput.amount
21304
21440
  };
21305
- const newDirectRefundSigningJob = newDirectRefundTx ? {
21306
- signingPublicKey,
21307
- rawTx: newDirectRefundTx.toBytes(),
21308
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
21309
- } : void 0;
21310
- const newDirectFromCpfpRefundSigningJob = newDirectFromCpfpRefundTx ? {
21311
- signingPublicKey,
21312
- rawTx: newDirectFromCpfpRefundTx.toBytes(),
21313
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
21314
- } : void 0;
21315
- const sparkClient = await this.connectionManager.createSparkClient(
21316
- this.config.getCoordinatorAddress()
21317
- );
21318
- const response = await sparkClient.extend_leaf_v2({
21319
- leafId: node.id,
21320
- ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
21321
- nodeTxSigningJob: getSigningJobProto(newNodeSigningJob),
21322
- directNodeTxSigningJob: newDirectNodeSigningJob ? getSigningJobProto(newDirectNodeSigningJob) : void 0,
21323
- refundTxSigningJob: getSigningJobProto(newCpfpRefundSigningJob),
21324
- directRefundTxSigningJob: newDirectRefundSigningJob ? getSigningJobProto(newDirectRefundSigningJob) : void 0,
21325
- directFromCpfpRefundTxSigningJob: newDirectFromCpfpRefundSigningJob ? getSigningJobProto(newDirectFromCpfpRefundSigningJob) : void 0
21326
- });
21327
- if (!response.nodeTxSigningResult || !response.refundTxSigningResult) {
21328
- throw new Error("Signing result does not exist");
21329
- }
21330
21441
  const keyDerivation = {
21331
21442
  type: "leaf" /* LEAF */,
21332
21443
  path: node.id
21333
21444
  };
21334
- const nodeUserSig = await this.config.signer.signFrost({
21335
- message: nodeSighash,
21445
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
21446
+ const { nodeTx: splitNodeTx, directNodeTx: splitNodeDirectTx } = createZeroTimelockNodeTx(parentTx);
21447
+ signingJobs.push({
21448
+ signingPublicKey,
21449
+ rawTx: splitNodeTx.toBytes(),
21450
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
21451
+ type: "split",
21452
+ parentTxOut: unsignedParentNodeOutput,
21453
+ leafId: node.id,
21336
21454
  keyDerivation,
21337
- publicKey: signingPublicKey,
21338
- verifyingKey: response.nodeTxSigningResult.verifyingKey,
21339
- selfCommitment: newNodeSigningJob.signingNonceCommitment,
21340
- statechainCommitments: response.nodeTxSigningResult.signingResult?.signingNonceCommitments,
21341
- adaptorPubKey: new Uint8Array()
21342
- });
21343
- const nodeSig = await this.config.signer.aggregateFrost({
21344
- message: nodeSighash,
21345
- statechainSignatures: response.nodeTxSigningResult.signingResult?.signatureShares,
21346
- statechainPublicKeys: response.nodeTxSigningResult.signingResult?.publicKeys,
21347
- verifyingKey: response.nodeTxSigningResult.verifyingKey,
21348
- statechainCommitments: response.nodeTxSigningResult.signingResult?.signingNonceCommitments,
21349
- selfCommitment: newNodeSigningJob.signingNonceCommitment,
21350
- publicKey: signingPublicKey,
21351
- selfSignature: nodeUserSig,
21352
- adaptorPubKey: new Uint8Array()
21455
+ verifyingKey: node.verifyingPublicKey
21353
21456
  });
21354
- let directNodeSig;
21355
- if (directNodeSighash && newDirectNodeSigningJob && response.directNodeTxSigningResult) {
21356
- const directNodeUserSig = await this.config.signer.signFrost({
21357
- message: directNodeSighash,
21457
+ if (splitNodeDirectTx) {
21458
+ signingJobs.push({
21459
+ signingPublicKey,
21460
+ rawTx: splitNodeDirectTx.toBytes(),
21461
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
21462
+ type: "directSplit",
21463
+ parentTxOut: unsignedParentNodeOutput,
21464
+ leafId: node.id,
21358
21465
  keyDerivation,
21359
- publicKey: signingPublicKey,
21360
- verifyingKey: response.directNodeTxSigningResult.verifyingKey,
21361
- selfCommitment: newDirectNodeSigningJob.signingNonceCommitment,
21362
- statechainCommitments: response.directNodeTxSigningResult.signingResult?.signingNonceCommitments,
21363
- adaptorPubKey: new Uint8Array()
21364
- });
21365
- directNodeSig = await this.config.signer.aggregateFrost({
21366
- message: directNodeSighash,
21367
- statechainSignatures: response.directNodeTxSigningResult.signingResult?.signatureShares,
21368
- statechainPublicKeys: response.directNodeTxSigningResult.signingResult?.publicKeys,
21369
- verifyingKey: response.directNodeTxSigningResult.verifyingKey,
21370
- statechainCommitments: response.directNodeTxSigningResult.signingResult?.signingNonceCommitments,
21371
- selfCommitment: newDirectNodeSigningJob.signingNonceCommitment,
21372
- publicKey: signingPublicKey,
21373
- selfSignature: directNodeUserSig,
21374
- adaptorPubKey: new Uint8Array()
21466
+ verifyingKey: node.verifyingPublicKey
21375
21467
  });
21376
21468
  }
21377
- const cpfpRefundUserSig = await this.config.signer.signFrost({
21378
- message: cpfpRefundSighash,
21469
+ const splitNodeOutput = splitNodeTx.getOutput(0);
21470
+ const splitNodeDirectOutput = splitNodeDirectTx.getOutput(0);
21471
+ if (!splitNodeDirectOutput.amount || !splitNodeDirectOutput.script) {
21472
+ throw new Error("Could not get split node output");
21473
+ }
21474
+ const unsignedSplitNodeOutput = {
21475
+ script: splitNodeDirectOutput.script,
21476
+ amount: splitNodeDirectOutput.amount
21477
+ };
21478
+ const { nodeTx: newNodeTx, directNodeTx: newDirectNodeTx } = createInitialTimelockNodeTx(splitNodeTx);
21479
+ signingJobs.push({
21480
+ signingPublicKey,
21481
+ rawTx: newNodeTx.toBytes(),
21482
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
21483
+ type: "node",
21484
+ parentTxOut: splitNodeOutput,
21485
+ leafId: node.id,
21379
21486
  keyDerivation,
21380
- publicKey: signingPublicKey,
21381
- verifyingKey: response.refundTxSigningResult.verifyingKey,
21382
- selfCommitment: newCpfpRefundSigningJob.signingNonceCommitment,
21383
- statechainCommitments: response.refundTxSigningResult.signingResult?.signingNonceCommitments,
21384
- adaptorPubKey: new Uint8Array()
21385
- });
21386
- const cpfpRefundSig = await this.config.signer.aggregateFrost({
21387
- message: cpfpRefundSighash,
21388
- statechainSignatures: response.refundTxSigningResult.signingResult?.signatureShares,
21389
- statechainPublicKeys: response.refundTxSigningResult.signingResult?.publicKeys,
21390
- verifyingKey: response.refundTxSigningResult.verifyingKey,
21391
- statechainCommitments: response.refundTxSigningResult.signingResult?.signingNonceCommitments,
21392
- selfCommitment: newCpfpRefundSigningJob.signingNonceCommitment,
21393
- publicKey: signingPublicKey,
21394
- selfSignature: cpfpRefundUserSig,
21395
- adaptorPubKey: new Uint8Array()
21487
+ verifyingKey: node.verifyingPublicKey
21396
21488
  });
21397
- let directRefundSig;
21398
- if (directRefundSighash && newDirectRefundSigningJob && response.directRefundTxSigningResult) {
21399
- const directRefundUserSig = await this.config.signer.signFrost({
21400
- message: directRefundSighash,
21401
- keyDerivation,
21402
- publicKey: signingPublicKey,
21403
- verifyingKey: response.directRefundTxSigningResult.verifyingKey,
21404
- selfCommitment: newDirectRefundSigningJob.signingNonceCommitment,
21405
- statechainCommitments: response.directRefundTxSigningResult.signingResult?.signingNonceCommitments,
21406
- adaptorPubKey: new Uint8Array()
21407
- });
21408
- directRefundSig = await this.config.signer.aggregateFrost({
21409
- message: directRefundSighash,
21410
- statechainSignatures: response.directRefundTxSigningResult.signingResult?.signatureShares,
21411
- statechainPublicKeys: response.directRefundTxSigningResult.signingResult?.publicKeys,
21412
- verifyingKey: response.directRefundTxSigningResult.verifyingKey,
21413
- statechainCommitments: response.directRefundTxSigningResult.signingResult?.signingNonceCommitments,
21414
- selfCommitment: newDirectRefundSigningJob.signingNonceCommitment,
21415
- publicKey: signingPublicKey,
21416
- selfSignature: directRefundUserSig,
21417
- adaptorPubKey: new Uint8Array()
21418
- });
21419
- }
21420
- let directFromCpfpRefundSig;
21421
- if (directFromCpfpRefundSighash && newDirectFromCpfpRefundSigningJob && response.directFromCpfpRefundTxSigningResult) {
21422
- const directFromCpfpRefundUserSig = await this.config.signer.signFrost({
21423
- message: directFromCpfpRefundSighash,
21489
+ if (newDirectNodeTx) {
21490
+ signingJobs.push({
21491
+ signingPublicKey,
21492
+ rawTx: newDirectNodeTx.toBytes(),
21493
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
21494
+ type: "directNode",
21495
+ parentTxOut: splitNodeOutput,
21496
+ leafId: node.id,
21424
21497
  keyDerivation,
21425
- publicKey: signingPublicKey,
21426
- verifyingKey: response.directFromCpfpRefundTxSigningResult.verifyingKey,
21427
- selfCommitment: newDirectFromCpfpRefundSigningJob.signingNonceCommitment,
21428
- statechainCommitments: response.directFromCpfpRefundTxSigningResult.signingResult?.signingNonceCommitments,
21429
- adaptorPubKey: new Uint8Array()
21430
- });
21431
- directFromCpfpRefundSig = await this.config.signer.aggregateFrost({
21432
- message: directFromCpfpRefundSighash,
21433
- statechainSignatures: response.directFromCpfpRefundTxSigningResult.signingResult?.signatureShares,
21434
- statechainPublicKeys: response.directFromCpfpRefundTxSigningResult.signingResult?.publicKeys,
21435
- verifyingKey: response.directFromCpfpRefundTxSigningResult.verifyingKey,
21436
- statechainCommitments: response.directFromCpfpRefundTxSigningResult.signingResult?.signingNonceCommitments,
21437
- selfCommitment: newDirectFromCpfpRefundSigningJob.signingNonceCommitment,
21438
- publicKey: signingPublicKey,
21439
- selfSignature: directFromCpfpRefundUserSig,
21440
- adaptorPubKey: new Uint8Array()
21441
- });
21442
- }
21443
- return await sparkClient.finalize_node_signatures_v2({
21444
- intent: 4 /* EXTEND */,
21445
- nodeSignatures: [
21446
- {
21447
- nodeId: response.leafId,
21448
- nodeTxSignature: nodeSig,
21449
- directNodeTxSignature: directNodeSig,
21450
- refundTxSignature: cpfpRefundSig,
21451
- directRefundTxSignature: directRefundSig,
21452
- directFromCpfpRefundTxSignature: directFromCpfpRefundSig
21453
- }
21454
- ]
21455
- });
21456
- }
21457
- async testonly_expireTimeLockNodeTx(node, parentNode) {
21458
- return await this.refreshTimelockNodesInternal(node, parentNode, true);
21459
- }
21460
- async testonly_expireTimeLockRefundtx(node) {
21461
- const nodeTx = getTxFromRawTxBytes(node.nodeTx);
21462
- const directNodeTx = node.directTx.length > 0 ? getTxFromRawTxBytes(node.directTx) : void 0;
21463
- const cpfpRefundTx = getTxFromRawTxBytes(node.refundTx);
21464
- const currSequence = cpfpRefundTx.getInput(0).sequence || 0;
21465
- const currTimelock = getCurrentTimelock(currSequence);
21466
- if (currTimelock <= 100) {
21467
- throw new ValidationError("Cannot expire timelock below 100", {
21468
- field: "currTimelock",
21469
- value: currTimelock,
21470
- expected: "Timelock greater than 100"
21498
+ verifyingKey: node.verifyingPublicKey
21471
21499
  });
21472
21500
  }
21473
- const nextSequence = TEST_UNILATERAL_SEQUENCE;
21474
- const nextDirectSequence = TEST_UNILATERAL_SEQUENCE + DIRECT_TIMELOCK_OFFSET;
21475
- const nodeOutput = nodeTx.getOutput(0);
21476
- if (!nodeOutput) {
21477
- throw Error("Could not get node output");
21478
- }
21479
- const keyDerivation = {
21480
- type: "leaf" /* LEAF */,
21481
- path: node.id
21482
- };
21483
- const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
21484
- const cpfpRefundOutPoint = {
21485
- txid: (0, import_utils4.hexToBytes)(getTxId(nodeTx)),
21486
- index: 0
21487
- };
21488
- let directRefundOutPoint;
21489
- if (directNodeTx) {
21490
- directRefundOutPoint = {
21491
- txid: (0, import_utils4.hexToBytes)(getTxId(directNodeTx)),
21492
- index: 0
21493
- };
21501
+ const newCpfpNodeOutput = newNodeTx.getOutput(0);
21502
+ if (!newCpfpNodeOutput) {
21503
+ throw Error("Could not get new cpfp node output");
21494
21504
  }
21505
+ const newDirectNodeOutput = newDirectNodeTx?.getOutput(0);
21495
21506
  const {
21496
- cpfpRefundTx: newCpfpRefundTx,
21507
+ cpfpRefundTx: newRefundTx,
21497
21508
  directRefundTx: newDirectRefundTx,
21498
21509
  directFromCpfpRefundTx: newDirectFromCpfpRefundTx
21499
- } = createRefundTxs({
21500
- sequence: nextSequence,
21501
- directSequence: nextDirectSequence,
21502
- input: cpfpRefundOutPoint,
21503
- directInput: directRefundOutPoint,
21504
- amountSats: nodeOutput.amount,
21510
+ } = createInitialTimelockRefundTxs({
21511
+ nodeTx: newNodeTx,
21512
+ directNodeTx: newDirectNodeTx,
21505
21513
  receivingPubkey: signingPublicKey,
21506
21514
  network: this.config.getNetwork()
21507
21515
  });
21508
- const signingJobs = [];
21509
21516
  signingJobs.push({
21510
21517
  signingPublicKey,
21511
- rawTx: newCpfpRefundTx.toBytes(),
21518
+ rawTx: newRefundTx.toBytes(),
21512
21519
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
21513
21520
  type: "cpfp",
21514
- parentTxOut: nodeOutput
21521
+ parentTxOut: newCpfpNodeOutput,
21522
+ leafId: node.id,
21523
+ keyDerivation,
21524
+ verifyingKey: node.verifyingPublicKey
21515
21525
  });
21516
- const directNodeTxOut = directNodeTx?.getOutput(0);
21517
- if (newDirectRefundTx && directNodeTxOut) {
21526
+ if (newDirectRefundTx && newDirectNodeOutput) {
21518
21527
  signingJobs.push({
21519
21528
  signingPublicKey,
21520
21529
  rawTx: newDirectRefundTx.toBytes(),
21521
21530
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
21522
21531
  type: "direct",
21523
- parentTxOut: directNodeTxOut
21532
+ parentTxOut: newDirectNodeOutput,
21533
+ leafId: node.id,
21534
+ keyDerivation,
21535
+ verifyingKey: node.verifyingPublicKey
21524
21536
  });
21525
21537
  }
21526
- if (newDirectFromCpfpRefundTx) {
21538
+ if (newDirectFromCpfpRefundTx && newDirectNodeOutput) {
21527
21539
  signingJobs.push({
21528
21540
  signingPublicKey,
21529
21541
  rawTx: newDirectFromCpfpRefundTx.toBytes(),
21530
21542
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
21531
21543
  type: "directFromCpfp",
21532
- parentTxOut: nodeOutput
21544
+ parentTxOut: newCpfpNodeOutput,
21545
+ leafId: node.id,
21546
+ keyDerivation,
21547
+ verifyingKey: node.verifyingPublicKey
21533
21548
  });
21534
21549
  }
21550
+ return signingJobs;
21551
+ }
21552
+ async renewZeroTimelockNodeTxn(node) {
21535
21553
  const sparkClient = await this.connectionManager.createSparkClient(
21536
21554
  this.config.getCoordinatorAddress()
21537
21555
  );
21538
- const response = await sparkClient.refresh_timelock_v2({
21539
- leafId: node.id,
21540
- ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
21541
- signingJobs: signingJobs.map(getSigningJobProto)
21556
+ const signingJobs = await this.createRenewZeroTimelockNodeSigningJobs(node);
21557
+ const statechainCommitments = await sparkClient.get_signing_commitments({
21558
+ nodeIds: [node.id],
21559
+ count: signingJobs.length
21542
21560
  });
21543
- if (response.signingResults.length !== signingJobs.length) {
21544
- throw Error(
21545
- `Expected ${signingJobs.length} signing results, got ${response.signingResults.length}`
21546
- );
21547
- }
21548
- let cpfpRefundSignature;
21549
- let directRefundSignature;
21550
- let directFromCpfpRefundSignature;
21551
- for (const [i, signingJob] of signingJobs.entries()) {
21552
- const signingResult = response.signingResults[i];
21553
- if (!signingResult) {
21554
- throw Error("Signing result does not exist");
21561
+ const mappedSigningJobs = signingJobs.map((signingJob, index) => {
21562
+ const signingNonceCommitments = statechainCommitments.signingCommitments[index]?.signingNonceCommitments;
21563
+ if (!signingNonceCommitments) {
21564
+ throw new ValidationError("Signing nonce commitments not found", {
21565
+ field: "signingNonceCommitments",
21566
+ value: signingNonceCommitments
21567
+ });
21555
21568
  }
21556
- const rawTx = getTxFromRawTxBytes(signingJob.rawTx);
21557
- const txOut = signingJob.parentTxOut;
21558
- const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
21559
- const userSignature = await this.config.signer.signFrost({
21560
- message: rawTxSighash,
21561
- keyDerivation,
21562
- publicKey: signingPublicKey,
21563
- verifyingKey: signingResult.verifyingKey,
21564
- selfCommitment: signingJob.signingNonceCommitment,
21565
- statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
21566
- adaptorPubKey: new Uint8Array()
21567
- });
21568
- const signature = await this.config.signer.aggregateFrost({
21569
- message: rawTxSighash,
21570
- statechainSignatures: signingResult.signingResult?.signatureShares,
21571
- statechainPublicKeys: signingResult.signingResult?.publicKeys,
21572
- verifyingKey: signingResult.verifyingKey,
21573
- statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
21574
- selfCommitment: signingJob.signingNonceCommitment,
21575
- publicKey: signingPublicKey,
21576
- selfSignature: userSignature,
21577
- adaptorPubKey: new Uint8Array()
21578
- });
21579
- if (signingJob.type === "cpfp") {
21580
- cpfpRefundSignature = signature;
21581
- } else if (signingJob.type === "direct") {
21582
- directRefundSignature = signature;
21583
- } else if (signingJob.type === "directFromCpfp") {
21584
- directFromCpfpRefundSignature = signature;
21569
+ return {
21570
+ ...signingJob,
21571
+ signingNonceCommitments
21572
+ };
21573
+ });
21574
+ const userSignedTxSigningJobs = await this.signingService.signSigningJobs(mappedSigningJobs);
21575
+ const renewZeroTimelockNodeSigningJob = {
21576
+ nodeTxSigningJob: userSignedTxSigningJobs.get("node"),
21577
+ refundTxSigningJob: userSignedTxSigningJobs.get("cpfp"),
21578
+ directNodeTxSigningJob: userSignedTxSigningJobs.get("directNode"),
21579
+ directRefundTxSigningJob: void 0,
21580
+ directFromCpfpRefundTxSigningJob: userSignedTxSigningJobs.get("directFromCpfp")
21581
+ };
21582
+ const response = await sparkClient.renew_leaf({
21583
+ leafId: node.id,
21584
+ signingJobs: {
21585
+ $case: "renewNodeZeroTimelockSigningJob",
21586
+ renewNodeZeroTimelockSigningJob: renewZeroTimelockNodeSigningJob
21585
21587
  }
21588
+ });
21589
+ if (response.renewResult?.$case !== "renewNodeZeroTimelockResult" || !response.renewResult?.renewNodeZeroTimelockResult.node) {
21590
+ throw new ValidationError("Unexpected renew result", {
21591
+ field: "renewResult",
21592
+ value: response.renewResult
21593
+ });
21586
21594
  }
21587
- const result = await sparkClient.finalize_node_signatures_v2({
21588
- intent: 3 /* REFRESH */,
21589
- nodeSignatures: [
21590
- {
21591
- nodeId: node.id,
21592
- nodeTxSignature: new Uint8Array(),
21593
- directNodeTxSignature: new Uint8Array(),
21594
- refundTxSignature: cpfpRefundSignature,
21595
- directRefundTxSignature: directRefundSignature,
21596
- directFromCpfpRefundTxSignature: directFromCpfpRefundSignature
21597
- }
21598
- ]
21595
+ return response.renewResult.renewNodeZeroTimelockResult.node;
21596
+ }
21597
+ async createRenewZeroTimelockNodeSigningJobs(node) {
21598
+ const signingJobs = [];
21599
+ const keyDerivation = {
21600
+ type: "leaf" /* LEAF */,
21601
+ path: node.id
21602
+ };
21603
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
21604
+ const nodeTx = getTxFromRawTxBytes(node.nodeTx);
21605
+ const { nodeTx: newNodeTx, directNodeTx: newDirectNodeTx } = createZeroTimelockNodeTx(nodeTx);
21606
+ signingJobs.push({
21607
+ signingPublicKey,
21608
+ rawTx: newNodeTx.toBytes(),
21609
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
21610
+ type: "node",
21611
+ parentTxOut: nodeTx.getOutput(0),
21612
+ leafId: node.id,
21613
+ keyDerivation,
21614
+ verifyingKey: node.verifyingPublicKey
21599
21615
  });
21600
- return result;
21616
+ signingJobs.push({
21617
+ signingPublicKey,
21618
+ rawTx: newDirectNodeTx.toBytes(),
21619
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
21620
+ type: "directNode",
21621
+ parentTxOut: nodeTx.getOutput(0),
21622
+ leafId: node.id,
21623
+ keyDerivation,
21624
+ verifyingKey: node.verifyingPublicKey
21625
+ });
21626
+ const { cpfpRefundTx, directFromCpfpRefundTx } = createInitialTimelockRefundTxs({
21627
+ nodeTx: newNodeTx,
21628
+ directNodeTx: newDirectNodeTx,
21629
+ receivingPubkey: signingPublicKey,
21630
+ network: this.config.getNetwork()
21631
+ });
21632
+ signingJobs.push({
21633
+ signingPublicKey,
21634
+ rawTx: cpfpRefundTx.toBytes(),
21635
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
21636
+ type: "cpfp",
21637
+ parentTxOut: newNodeTx.getOutput(0),
21638
+ leafId: node.id,
21639
+ keyDerivation,
21640
+ verifyingKey: node.verifyingPublicKey
21641
+ });
21642
+ if (!directFromCpfpRefundTx) {
21643
+ throw new Error("Could not create direct refund transactions");
21644
+ }
21645
+ signingJobs.push({
21646
+ signingPublicKey,
21647
+ rawTx: directFromCpfpRefundTx.toBytes(),
21648
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
21649
+ type: "directFromCpfp",
21650
+ parentTxOut: newNodeTx.getOutput(0),
21651
+ leafId: node.id,
21652
+ keyDerivation,
21653
+ verifyingKey: node.verifyingPublicKey
21654
+ });
21655
+ return signingJobs;
21601
21656
  }
21602
21657
  };
21603
21658
 
@@ -21639,71 +21694,6 @@ var CoopExitService = class extends BaseTransferService {
21639
21694
  directFromCpfpSignaturesMap
21640
21695
  };
21641
21696
  }
21642
- createConnectorRefundTransactions(sequence, directSequence, cpfpNodeOutPoint, directNodeOutPoint, connectorOutput, amountSats, receiverPubKey) {
21643
- const cpfpRefundTx = new import_btc_signer3.Transaction();
21644
- if (!cpfpNodeOutPoint.txid || cpfpNodeOutPoint.index === void 0) {
21645
- throw new ValidationError("Invalid CPFP node outpoint", {
21646
- field: "cpfpNodeOutPoint",
21647
- value: { txid: cpfpNodeOutPoint.txid, index: cpfpNodeOutPoint.index },
21648
- expected: "Both txid and index must be defined"
21649
- });
21650
- }
21651
- cpfpRefundTx.addInput({
21652
- txid: cpfpNodeOutPoint.txid,
21653
- index: cpfpNodeOutPoint.index,
21654
- sequence
21655
- });
21656
- cpfpRefundTx.addInput(connectorOutput);
21657
- const receiverScript = getP2TRScriptFromPublicKey(
21658
- receiverPubKey,
21659
- this.config.getNetwork()
21660
- );
21661
- cpfpRefundTx.addOutput({
21662
- script: receiverScript,
21663
- amount: amountSats
21664
- });
21665
- let directRefundTx;
21666
- let directFromCpfpRefundTx;
21667
- if (directNodeOutPoint) {
21668
- if (!directNodeOutPoint.txid || directNodeOutPoint.index === void 0) {
21669
- throw new ValidationError("Invalid direct node outpoint", {
21670
- field: "directNodeOutPoint",
21671
- value: {
21672
- txid: directNodeOutPoint.txid,
21673
- index: directNodeOutPoint.index
21674
- },
21675
- expected: "Both txid and index must be defined"
21676
- });
21677
- }
21678
- directRefundTx = new import_btc_signer3.Transaction();
21679
- directRefundTx.addInput({
21680
- txid: directNodeOutPoint.txid,
21681
- index: directNodeOutPoint.index,
21682
- sequence: directSequence
21683
- });
21684
- directRefundTx.addInput(connectorOutput);
21685
- directRefundTx.addOutput({
21686
- script: receiverScript,
21687
- amount: maybeApplyFee(amountSats)
21688
- });
21689
- directFromCpfpRefundTx = new import_btc_signer3.Transaction();
21690
- directFromCpfpRefundTx.addInput({
21691
- txid: cpfpNodeOutPoint.txid,
21692
- index: cpfpNodeOutPoint.index,
21693
- sequence: directSequence
21694
- });
21695
- directFromCpfpRefundTx.addInput(connectorOutput);
21696
- directFromCpfpRefundTx.addOutput({
21697
- script: receiverScript,
21698
- amount: maybeApplyFee(amountSats)
21699
- });
21700
- }
21701
- return {
21702
- cpfpRefundTx,
21703
- directRefundTx,
21704
- directFromCpfpRefundTx
21705
- };
21706
- }
21707
21697
  async signCoopExitRefunds(leaves, exitTxId, connectorOutputs, receiverPubKey, transferId) {
21708
21698
  if (leaves.length !== connectorOutputs.length) {
21709
21699
  throw new ValidationError(
@@ -21737,29 +21727,39 @@ var CoopExitService = class extends BaseTransferService {
21737
21727
  expected: "Valid connector output"
21738
21728
  });
21739
21729
  }
21730
+ const nodeTx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
21731
+ let directNodeTx;
21732
+ if (leaf.leaf.directTx.length > 0) {
21733
+ directNodeTx = getTxFromRawTxBytes(leaf.leaf.directTx);
21734
+ }
21740
21735
  const currentRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
21741
- const sequence = currentRefundTx.getInput(0).sequence;
21742
- if (!sequence) {
21736
+ if (!currentRefundTx) {
21737
+ throw new ValidationError("Invalid refund transaction", {
21738
+ field: "currentRefundTx",
21739
+ value: currentRefundTx,
21740
+ expected: "Non-null refund transaction"
21741
+ });
21742
+ }
21743
+ const currentSequence = currentRefundTx.getInput(0).sequence;
21744
+ if (!currentSequence) {
21743
21745
  throw new ValidationError("Invalid refund transaction", {
21744
21746
  field: "sequence",
21745
21747
  value: currentRefundTx.getInput(0),
21746
21748
  expected: "Non-null sequence"
21747
21749
  });
21748
21750
  }
21749
- const { nextSequence, nextDirectSequence } = getNextTransactionSequence(sequence);
21750
21751
  let currentDirectRefundTx;
21751
21752
  if (leaf.leaf.directRefundTx.length > 0) {
21752
21753
  currentDirectRefundTx = getTxFromRawTxBytes(leaf.leaf.directRefundTx);
21753
21754
  }
21754
- const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = this.createConnectorRefundTransactions(
21755
- nextSequence,
21756
- nextDirectSequence,
21757
- currentRefundTx.getInput(0),
21758
- currentDirectRefundTx?.getInput(0),
21755
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createConnectorRefundTxs({
21756
+ nodeTx,
21757
+ directNodeTx,
21758
+ sequence: currentSequence,
21759
21759
  connectorOutput,
21760
- BigInt(leaf.leaf.value),
21761
- receiverPubKey
21762
- );
21760
+ receivingPubkey: receiverPubKey,
21761
+ network: this.config.getNetwork()
21762
+ });
21763
21763
  const signingNonceCommitment = await this.config.signer.getRandomSigningCommitment();
21764
21764
  const directSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
21765
21765
  const directFromCpfpSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
@@ -21865,13 +21865,13 @@ var CoopExitService = class extends BaseTransferService {
21865
21865
  // src/services/deposit.ts
21866
21866
  var import_secp256k14 = require("@noble/curves/secp256k1");
21867
21867
  var import_sha26 = require("@noble/hashes/sha2");
21868
- var import_utils6 = require("@noble/hashes/utils");
21869
- var import_btc_signer4 = require("@scure/btc-signer");
21870
- var import_utils7 = require("@scure/btc-signer/utils");
21868
+ var import_utils7 = require("@noble/hashes/utils");
21869
+ var import_btc_signer2 = require("@scure/btc-signer");
21870
+ var import_utils8 = require("@scure/btc-signer/utils");
21871
21871
 
21872
21872
  // src/utils/keys.ts
21873
21873
  var import_secp256k13 = require("@noble/curves/secp256k1");
21874
- var import_utils5 = require("@noble/curves/utils");
21874
+ var import_utils6 = require("@noble/curves/utils");
21875
21875
  function addPublicKeys(a, b) {
21876
21876
  if (a.length !== 33 || b.length !== 33) {
21877
21877
  throw new ValidationError("Public keys must be 33 bytes", {
@@ -21928,7 +21928,7 @@ function addPrivateKeys(a, b) {
21928
21928
  const privA = import_secp256k13.secp256k1.utils.normPrivateKeyToScalar(a);
21929
21929
  const privB = import_secp256k13.secp256k1.utils.normPrivateKeyToScalar(b);
21930
21930
  const sum2 = (privA + privB) % import_secp256k13.secp256k1.CURVE.n;
21931
- return (0, import_utils5.numberToBytesBE)(sum2, 32);
21931
+ return (0, import_utils6.numberToBytesBE)(sum2, 32);
21932
21932
  }
21933
21933
  function subtractPrivateKeys(a, b) {
21934
21934
  if (a.length !== 32 || b.length !== 32) {
@@ -21941,7 +21941,7 @@ function subtractPrivateKeys(a, b) {
21941
21941
  const privA = import_secp256k13.secp256k1.utils.normPrivateKeyToScalar(a);
21942
21942
  const privB = import_secp256k13.secp256k1.utils.normPrivateKeyToScalar(b);
21943
21943
  const sum2 = (import_secp256k13.secp256k1.CURVE.n - privB + privA) % import_secp256k13.secp256k1.CURVE.n;
21944
- return (0, import_utils5.numberToBytesBE)(sum2, 32);
21944
+ return (0, import_utils6.numberToBytesBE)(sum2, 32);
21945
21945
  }
21946
21946
  function sumOfPrivateKeys(keys) {
21947
21947
  return keys.reduce((sum2, key) => {
@@ -22008,7 +22008,7 @@ var DepositService = class {
22008
22008
  operatorPubkey,
22009
22009
  address.address
22010
22010
  );
22011
- const taprootKey = (0, import_btc_signer4.p2tr)(
22011
+ const taprootKey = (0, import_btc_signer2.p2tr)(
22012
22012
  operatorPubkey.slice(1, 33),
22013
22013
  void 0,
22014
22014
  getNetwork(this.config.getNetwork())
@@ -22032,7 +22032,7 @@ var DepositService = class {
22032
22032
  if (operator.identifier === this.config.getCoordinatorIdentifier() && !verifyCoordinatorProof) {
22033
22033
  continue;
22034
22034
  }
22035
- const operatorPubkey2 = (0, import_utils6.hexToBytes)(operator.identityPublicKey);
22035
+ const operatorPubkey2 = (0, import_utils7.hexToBytes)(operator.identityPublicKey);
22036
22036
  const operatorSig = address.depositAddressProof.addressSignatures[operator.identifier];
22037
22037
  if (!operatorSig) {
22038
22038
  throw new ValidationError("Operator signature not found", {
@@ -22151,38 +22151,18 @@ var DepositService = class {
22151
22151
  expected: "Valid output index"
22152
22152
  });
22153
22153
  }
22154
- const script = output.script;
22155
- const amount = output.amount;
22156
- if (!script || !amount) {
22157
- throw new ValidationError("No script or amount found in deposit tx", {
22158
- field: "output",
22159
- value: output,
22160
- expected: "Output with script and amount"
22161
- });
22162
- }
22163
- const depositOutPoint = {
22164
- txid: (0, import_utils6.hexToBytes)(getTxId(depositTx)),
22165
- index: vout
22166
- };
22167
- const depositTxOut = {
22168
- script,
22169
- amount
22170
- };
22171
- const [cpfpRootTx, directRootTx] = createRootTx(
22172
- depositOutPoint,
22173
- depositTxOut
22154
+ const { nodeTx: cpfpRootTx, directNodeTx: directRootTx } = createRootNodeTx(
22155
+ depositTx,
22156
+ vout
22174
22157
  );
22175
22158
  const cpfpRootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
22176
22159
  const directRootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
22177
22160
  const cpfpRootTxSighash = getSigHashFromTx(cpfpRootTx, 0, output);
22178
22161
  const directRootTxSighash = getSigHashFromTx(directRootTx, 0, output);
22179
22162
  const signingPubKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
22180
- const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
22181
- sequence: INITIAL_SEQUENCE,
22182
- directSequence: INITIAL_DIRECT_SEQUENCE,
22183
- input: { txid: (0, import_utils6.hexToBytes)(getTxId(cpfpRootTx)), index: 0 },
22184
- directInput: { txid: (0, import_utils6.hexToBytes)(getTxId(directRootTx)), index: 0 },
22185
- amountSats: amount,
22163
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createInitialTimelockRefundTxs({
22164
+ nodeTx: cpfpRootTx,
22165
+ directNodeTx: directRootTx,
22186
22166
  receivingPubkey: signingPubKey,
22187
22167
  network: this.config.getNetwork()
22188
22168
  });
@@ -22311,7 +22291,7 @@ var DepositService = class {
22311
22291
  }
22312
22292
  );
22313
22293
  }
22314
- if (!(0, import_utils7.equalBytes)(treeResp.rootNodeSignatureShares.verifyingKey, verifyingKey)) {
22294
+ if (!(0, import_utils8.equalBytes)(treeResp.rootNodeSignatureShares.verifyingKey, verifyingKey)) {
22315
22295
  throw new ValidationError("Verifying key mismatch", {
22316
22296
  field: "verifyingKey",
22317
22297
  value: treeResp.rootNodeSignatureShares.verifyingKey,
@@ -22474,22 +22454,12 @@ var DepositService = class {
22474
22454
  expected: "Output with script and amount"
22475
22455
  });
22476
22456
  }
22477
- const depositOutPoint = {
22478
- txid: (0, import_utils6.hexToBytes)(getTxId(depositTx)),
22479
- index: vout
22480
- };
22481
- const depositTxOut = {
22482
- script,
22483
- amount
22484
- };
22485
- const [cpfpRootTx, _] = createRootTx(depositOutPoint, depositTxOut);
22457
+ const { nodeTx: cpfpRootTx } = createRootNodeTx(depositTx, vout);
22486
22458
  const cpfpRootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
22487
22459
  const cpfpRootTxSighash = getSigHashFromTx(cpfpRootTx, 0, output);
22488
22460
  const signingPubKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
22489
- const { cpfpRefundTx } = createRefundTxs({
22490
- sequence: INITIAL_SEQUENCE,
22491
- input: { txid: (0, import_utils6.hexToBytes)(getTxId(cpfpRootTx)), index: 0 },
22492
- amountSats: amount,
22461
+ const { cpfpRefundTx } = createInitialTimelockRefundTxs({
22462
+ nodeTx: cpfpRootTx,
22493
22463
  receivingPubkey: signingPubKey,
22494
22464
  network: this.config.getNetwork()
22495
22465
  });
@@ -22558,7 +22528,7 @@ var DepositService = class {
22558
22528
  }
22559
22529
  );
22560
22530
  }
22561
- if (!(0, import_utils7.equalBytes)(treeResp.rootNodeSignatureShares.verifyingKey, verifyingKey)) {
22531
+ if (!(0, import_utils8.equalBytes)(treeResp.rootNodeSignatureShares.verifyingKey, verifyingKey)) {
22562
22532
  throw new ValidationError("Verifying key mismatch", {
22563
22533
  field: "verifyingKey",
22564
22534
  value: treeResp.rootNodeSignatureShares.verifyingKey,
@@ -22634,7 +22604,7 @@ var DepositService = class {
22634
22604
 
22635
22605
  // src/services/lightning.ts
22636
22606
  var import_secp256k15 = require("@noble/curves/secp256k1");
22637
- var import_utils8 = require("@noble/curves/utils");
22607
+ var import_utils9 = require("@noble/curves/utils");
22638
22608
  var import_sha27 = require("@noble/hashes/sha2");
22639
22609
  var import_uuidv73 = require("uuidv7");
22640
22610
 
@@ -22719,8 +22689,8 @@ var LightningService = class {
22719
22689
  }) {
22720
22690
  const crypto = getCrypto();
22721
22691
  const randBytes = crypto.getRandomValues(new Uint8Array(32));
22722
- const preimage = (0, import_utils8.numberToBytesBE)(
22723
- (0, import_utils8.bytesToNumberBE)(randBytes) % import_secp256k15.secp256k1.CURVE.n,
22692
+ const preimage = (0, import_utils9.numberToBytesBE)(
22693
+ (0, import_utils9.bytesToNumberBE)(randBytes) % import_secp256k15.secp256k1.CURVE.n,
22724
22694
  32
22725
22695
  );
22726
22696
  return await this.createLightningInvoiceWithPreImage({
@@ -22775,12 +22745,12 @@ var LightningService = class {
22775
22745
  const sparkClient = await this.connectionManager.createSparkClient(
22776
22746
  operator.address
22777
22747
  );
22778
- const userIdentityPublicKey = receiverIdentityPubkey ? (0, import_utils8.hexToBytes)(receiverIdentityPubkey) : await this.config.signer.getIdentityPublicKey();
22748
+ const userIdentityPublicKey = receiverIdentityPubkey ? (0, import_utils9.hexToBytes)(receiverIdentityPubkey) : await this.config.signer.getIdentityPublicKey();
22779
22749
  try {
22780
22750
  await sparkClient.store_preimage_share({
22781
22751
  paymentHash,
22782
22752
  preimageShare: {
22783
- secretShare: (0, import_utils8.numberToBytesBE)(share.share, 32),
22753
+ secretShare: (0, import_utils9.numberToBytesBE)(share.share, 32),
22784
22754
  proofs: share.proofs
22785
22755
  },
22786
22756
  threshold: this.config.getThreshold(),
@@ -22997,14 +22967,14 @@ var LightningService = class {
22997
22967
  };
22998
22968
 
22999
22969
  // src/services/token-transactions.ts
23000
- var import_utils12 = require("@noble/curves/utils");
23001
- var import_utils13 = require("@noble/hashes/utils");
22970
+ var import_utils13 = require("@noble/curves/utils");
22971
+ var import_utils14 = require("@noble/hashes/utils");
23002
22972
 
23003
22973
  // src/utils/address.ts
23004
22974
  var import_wire5 = require("@bufbuild/protobuf/wire");
23005
22975
  var import_secp256k16 = require("@noble/curves/secp256k1");
23006
- var import_utils9 = require("@noble/curves/utils");
23007
- var import_utils10 = require("@noble/hashes/utils");
22976
+ var import_utils10 = require("@noble/curves/utils");
22977
+ var import_utils11 = require("@noble/hashes/utils");
23008
22978
  var import_base3 = require("@scure/base");
23009
22979
  var import_uuidv74 = require("uuidv7");
23010
22980
 
@@ -23196,7 +23166,7 @@ function encodeSparkAddress(payload) {
23196
23166
  function encodeSparkAddressWithSignature(payload, signature) {
23197
23167
  try {
23198
23168
  isValidPublicKey(payload.identityPublicKey);
23199
- const identityPublicKey = (0, import_utils10.hexToBytes)(payload.identityPublicKey);
23169
+ const identityPublicKey = (0, import_utils11.hexToBytes)(payload.identityPublicKey);
23200
23170
  let sparkInvoiceFields;
23201
23171
  if (payload.sparkInvoiceFields) {
23202
23172
  validateSparkInvoiceFields(payload.sparkInvoiceFields);
@@ -23240,8 +23210,8 @@ function decodeSparkAddress(address, network) {
23240
23210
  const decoded = bech32mDecode(address);
23241
23211
  const payload = SparkAddress.decode(import_base3.bech32m.fromWords(decoded.words));
23242
23212
  const { identityPublicKey, sparkInvoiceFields, signature } = payload;
23243
- const identityPubkeyHex = (0, import_utils10.bytesToHex)(identityPublicKey);
23244
- const signatureHex = signature ? (0, import_utils10.bytesToHex)(signature) : void 0;
23213
+ const identityPubkeyHex = (0, import_utils11.bytesToHex)(identityPublicKey);
23214
+ const signatureHex = signature ? (0, import_utils11.bytesToHex)(signature) : void 0;
23245
23215
  isValidPublicKey(identityPubkeyHex);
23246
23216
  return {
23247
23217
  identityPublicKey: identityPubkeyHex,
@@ -23251,10 +23221,10 @@ function decodeSparkAddress(address, network) {
23251
23221
  id: import_uuidv74.UUID.ofInner(sparkInvoiceFields.id).toString(),
23252
23222
  paymentType: sparkInvoiceFields.paymentType ? sparkInvoiceFields.paymentType.$case === "tokensPayment" ? {
23253
23223
  type: "tokens",
23254
- tokenIdentifier: sparkInvoiceFields.paymentType.tokensPayment.tokenIdentifier ? (0, import_utils10.bytesToHex)(
23224
+ tokenIdentifier: sparkInvoiceFields.paymentType.tokensPayment.tokenIdentifier ? (0, import_utils11.bytesToHex)(
23255
23225
  sparkInvoiceFields.paymentType.tokensPayment.tokenIdentifier
23256
23226
  ) : void 0,
23257
- amount: sparkInvoiceFields.paymentType.tokensPayment.amount ? (0, import_utils9.bytesToNumberBE)(
23227
+ amount: sparkInvoiceFields.paymentType.tokensPayment.amount ? (0, import_utils10.bytesToNumberBE)(
23258
23228
  sparkInvoiceFields.paymentType.tokensPayment.amount
23259
23229
  ) : void 0
23260
23230
  } : sparkInvoiceFields.paymentType.$case === "satsPayment" ? {
@@ -23262,7 +23232,7 @@ function decodeSparkAddress(address, network) {
23262
23232
  amount: sparkInvoiceFields.paymentType.satsPayment.amount
23263
23233
  } : void 0 : void 0,
23264
23234
  memo: sparkInvoiceFields.memo,
23265
- senderPublicKey: sparkInvoiceFields.senderPublicKey ? (0, import_utils10.bytesToHex)(sparkInvoiceFields.senderPublicKey) : void 0,
23235
+ senderPublicKey: sparkInvoiceFields.senderPublicKey ? (0, import_utils11.bytesToHex)(sparkInvoiceFields.senderPublicKey) : void 0,
23266
23236
  expiryTime: sparkInvoiceFields.expiryTime
23267
23237
  },
23268
23238
  signature: signatureHex
@@ -23363,7 +23333,7 @@ function validateSparkInvoiceFields(sparkInvoiceFields) {
23363
23333
  }
23364
23334
  if (senderPublicKey) {
23365
23335
  try {
23366
- isValidPublicKey((0, import_utils10.bytesToHex)(senderPublicKey));
23336
+ isValidPublicKey((0, import_utils11.bytesToHex)(senderPublicKey));
23367
23337
  } catch (error) {
23368
23338
  throw new ValidationError(
23369
23339
  "Invalid sender public key",
@@ -23414,7 +23384,7 @@ function validateSparkInvoiceFields(sparkInvoiceFields) {
23414
23384
  value: tokensAmount
23415
23385
  });
23416
23386
  }
23417
- const tokensAmountBigInt = (0, import_utils9.bytesToNumberBE)(tokensAmount);
23387
+ const tokensAmountBigInt = (0, import_utils10.bytesToNumberBE)(tokensAmount);
23418
23388
  if (tokensAmountBigInt < 0 || tokensAmountBigInt > MAX_UINT128) {
23419
23389
  throw new ValidationError(
23420
23390
  "Asset amount must be between 0 and MAX_UINT128",
@@ -27705,11 +27675,11 @@ function validateTokenTransaction(finalTokenTransaction, partialTokenTransaction
27705
27675
  }
27706
27676
 
27707
27677
  // src/utils/token-transactions.ts
27708
- var import_utils11 = require("@noble/curves/utils");
27678
+ var import_utils12 = require("@noble/curves/utils");
27709
27679
  function sumAvailableTokens(outputs) {
27710
27680
  try {
27711
27681
  return outputs.reduce(
27712
- (sum2, output) => sum2 + BigInt((0, import_utils11.bytesToNumberBE)(output.output.tokenAmount)),
27682
+ (sum2, output) => sum2 + BigInt((0, import_utils12.bytesToNumberBE)(output.output.tokenAmount)),
27713
27683
  BigInt(0)
27714
27684
  );
27715
27685
  } catch (error) {
@@ -27740,7 +27710,7 @@ function filterTokenBalanceForTokenIdentifier(tokenBalances, tokenIdentifier) {
27740
27710
  }
27741
27711
  const tokenIdentifierBytes = decodeBech32mTokenIdentifier(tokenIdentifier).tokenIdentifier;
27742
27712
  const tokenBalance = [...tokenBalances.entries()].find(
27743
- ([, info]) => (0, import_utils11.equalBytes)(info.tokenMetadata.rawTokenIdentifier, tokenIdentifierBytes)
27713
+ ([, info]) => (0, import_utils12.equalBytes)(info.tokenMetadata.rawTokenIdentifier, tokenIdentifierBytes)
27744
27714
  );
27745
27715
  if (!tokenBalance) {
27746
27716
  return {
@@ -27834,14 +27804,14 @@ var TokenTransactionService = class {
27834
27804
  }
27835
27805
  if (receiverAddress.sparkInvoiceFields) {
27836
27806
  return {
27837
- receiverPublicKey: (0, import_utils13.hexToBytes)(receiverAddress.identityPublicKey),
27807
+ receiverPublicKey: (0, import_utils14.hexToBytes)(receiverAddress.identityPublicKey),
27838
27808
  rawTokenIdentifier,
27839
27809
  tokenAmount: transfer.tokenAmount,
27840
27810
  sparkInvoice: transfer.receiverSparkAddress
27841
27811
  };
27842
27812
  }
27843
27813
  return {
27844
- receiverPublicKey: (0, import_utils13.hexToBytes)(receiverAddress.identityPublicKey),
27814
+ receiverPublicKey: (0, import_utils14.hexToBytes)(receiverAddress.identityPublicKey),
27845
27815
  rawTokenIdentifier,
27846
27816
  tokenAmount: transfer.tokenAmount
27847
27817
  };
@@ -27871,7 +27841,7 @@ var TokenTransactionService = class {
27871
27841
  (output) => ({
27872
27842
  ownerPublicKey: output.receiverPublicKey,
27873
27843
  tokenIdentifier: output.rawTokenIdentifier,
27874
- tokenAmount: (0, import_utils12.numberToBytesBE)(output.tokenAmount, 16)
27844
+ tokenAmount: (0, import_utils13.numberToBytesBE)(output.tokenAmount, 16)
27875
27845
  })
27876
27846
  );
27877
27847
  if (availableTokenAmount > totalRequestedAmount) {
@@ -27880,7 +27850,7 @@ var TokenTransactionService = class {
27880
27850
  tokenOutputs.push({
27881
27851
  ownerPublicKey: await this.config.signer.getIdentityPublicKey(),
27882
27852
  tokenIdentifier: firstTokenIdentifierBytes,
27883
- tokenAmount: (0, import_utils12.numberToBytesBE)(changeAmount, 16)
27853
+ tokenAmount: (0, import_utils13.numberToBytesBE)(changeAmount, 16)
27884
27854
  });
27885
27855
  }
27886
27856
  return {
@@ -27907,7 +27877,7 @@ var TokenTransactionService = class {
27907
27877
  for (const [_, operator] of Object.entries(
27908
27878
  this.config.getSigningOperators()
27909
27879
  )) {
27910
- operatorKeys.push((0, import_utils13.hexToBytes)(operator.identityPublicKey));
27880
+ operatorKeys.push((0, import_utils14.hexToBytes)(operator.identityPublicKey));
27911
27881
  }
27912
27882
  return operatorKeys;
27913
27883
  }
@@ -27924,7 +27894,7 @@ var TokenTransactionService = class {
27924
27894
  finalTokenTransactionHash,
27925
27895
  signingOperators
27926
27896
  );
27927
- return (0, import_utils12.bytesToHex)(finalTokenTransactionHash);
27897
+ return (0, import_utils13.bytesToHex)(finalTokenTransactionHash);
27928
27898
  }
27929
27899
  async startTokenTransaction(tokenTransaction, signingOperators, outputsToSpendSigningPublicKeys, outputsToSpendCommitments) {
27930
27900
  const sparkClient = await this.connectionManager.createSparkTokenClient(
@@ -28099,10 +28069,10 @@ var TokenTransactionService = class {
28099
28069
  });
28100
28070
  }
28101
28071
  for (const ownerPublicKey of ownerPublicKeys) {
28102
- isValidPublicKey((0, import_utils12.bytesToHex)(ownerPublicKey));
28072
+ isValidPublicKey((0, import_utils13.bytesToHex)(ownerPublicKey));
28103
28073
  }
28104
28074
  for (const issuerPublicKey of issuerPublicKeys) {
28105
- isValidPublicKey((0, import_utils12.bytesToHex)(issuerPublicKey));
28075
+ isValidPublicKey((0, import_utils13.bytesToHex)(issuerPublicKey));
28106
28076
  }
28107
28077
  for (const tokenIdentifier of tokenIdentifiers) {
28108
28078
  if (tokenIdentifier.length !== 32) {
@@ -28170,8 +28140,8 @@ var TokenTransactionService = class {
28170
28140
  this.config.getCoordinatorAddress()
28171
28141
  );
28172
28142
  let queryParams = {
28173
- issuerPublicKeys: issuerPublicKeys?.map(import_utils13.hexToBytes),
28174
- ownerPublicKeys: ownerPublicKeys?.map(import_utils13.hexToBytes),
28143
+ issuerPublicKeys: issuerPublicKeys?.map(import_utils14.hexToBytes),
28144
+ ownerPublicKeys: ownerPublicKeys?.map(import_utils14.hexToBytes),
28175
28145
  tokenIdentifiers: tokenIdentifiers?.map((identifier) => {
28176
28146
  const { tokenIdentifier } = decodeBech32mTokenIdentifier(
28177
28147
  identifier,
@@ -28179,7 +28149,7 @@ var TokenTransactionService = class {
28179
28149
  );
28180
28150
  return tokenIdentifier;
28181
28151
  }),
28182
- tokenTransactionHashes: tokenTransactionHashes?.map(import_utils13.hexToBytes),
28152
+ tokenTransactionHashes: tokenTransactionHashes?.map(import_utils14.hexToBytes),
28183
28153
  outputIds: outputIds || [],
28184
28154
  limit: pageSize,
28185
28155
  offset
@@ -28214,7 +28184,7 @@ var TokenTransactionService = class {
28214
28184
  });
28215
28185
  }
28216
28186
  const exactMatch = tokenOutputs.find(
28217
- (item) => (0, import_utils12.bytesToNumberBE)(item.output.tokenAmount) === tokenAmount
28187
+ (item) => (0, import_utils13.bytesToNumberBE)(item.output.tokenAmount) === tokenAmount
28218
28188
  );
28219
28189
  if (exactMatch) {
28220
28190
  return [exactMatch];
@@ -28225,7 +28195,7 @@ var TokenTransactionService = class {
28225
28195
  for (const outputWithPreviousTransactionData of tokenOutputs) {
28226
28196
  if (remainingAmount <= 0n) break;
28227
28197
  selectedOutputs.push(outputWithPreviousTransactionData);
28228
- remainingAmount -= (0, import_utils12.bytesToNumberBE)(
28198
+ remainingAmount -= (0, import_utils13.bytesToNumberBE)(
28229
28199
  outputWithPreviousTransactionData.output.tokenAmount
28230
28200
  );
28231
28201
  }
@@ -28240,14 +28210,14 @@ var TokenTransactionService = class {
28240
28210
  sortTokenOutputsByStrategy(tokenOutputs, strategy) {
28241
28211
  if (strategy === "SMALL_FIRST") {
28242
28212
  tokenOutputs.sort((a, b) => {
28243
- const amountA = (0, import_utils12.bytesToNumberBE)(a.output.tokenAmount);
28244
- const amountB = (0, import_utils12.bytesToNumberBE)(b.output.tokenAmount);
28213
+ const amountA = (0, import_utils13.bytesToNumberBE)(a.output.tokenAmount);
28214
+ const amountB = (0, import_utils13.bytesToNumberBE)(b.output.tokenAmount);
28245
28215
  return amountA < amountB ? -1 : amountA > amountB ? 1 : 0;
28246
28216
  });
28247
28217
  } else {
28248
28218
  tokenOutputs.sort((a, b) => {
28249
- const amountA = (0, import_utils12.bytesToNumberBE)(a.output.tokenAmount);
28250
- const amountB = (0, import_utils12.bytesToNumberBE)(b.output.tokenAmount);
28219
+ const amountA = (0, import_utils13.bytesToNumberBE)(a.output.tokenAmount);
28220
+ const amountB = (0, import_utils13.bytesToNumberBE)(b.output.tokenAmount);
28251
28221
  return amountB < amountA ? -1 : amountB > amountA ? 1 : 0;
28252
28222
  });
28253
28223
  }
@@ -28255,7 +28225,7 @@ var TokenTransactionService = class {
28255
28225
  // Helper function for deciding if the signer public key is the identity public key
28256
28226
  async signMessageWithKey(message, publicKey) {
28257
28227
  const tokenSignatures = this.config.getTokenSignatures();
28258
- if ((0, import_utils12.bytesToHex)(publicKey) === (0, import_utils12.bytesToHex)(await this.config.signer.getIdentityPublicKey())) {
28228
+ if ((0, import_utils13.bytesToHex)(publicKey) === (0, import_utils13.bytesToHex)(await this.config.signer.getIdentityPublicKey())) {
28259
28229
  if (tokenSignatures === "SCHNORR") {
28260
28230
  return await this.config.signer.signSchnorrWithIdentityKey(message);
28261
28231
  } else {
@@ -28264,8 +28234,8 @@ var TokenTransactionService = class {
28264
28234
  } else {
28265
28235
  throw new ValidationError("Invalid public key", {
28266
28236
  field: "publicKey",
28267
- value: (0, import_utils12.bytesToHex)(publicKey),
28268
- expected: (0, import_utils12.bytesToHex)(await this.config.signer.getIdentityPublicKey())
28237
+ value: (0, import_utils13.bytesToHex)(publicKey),
28238
+ expected: (0, import_utils13.bytesToHex)(await this.config.signer.getIdentityPublicKey())
28269
28239
  });
28270
28240
  }
28271
28241
  }
@@ -28284,7 +28254,7 @@ var TokenTransactionService = class {
28284
28254
  }
28285
28255
  const payload = {
28286
28256
  finalTokenTransactionHash,
28287
- operatorIdentityPublicKey: (0, import_utils13.hexToBytes)(operator.identityPublicKey)
28257
+ operatorIdentityPublicKey: (0, import_utils14.hexToBytes)(operator.identityPublicKey)
28288
28258
  };
28289
28259
  const payloadHash = await hashOperatorSpecificTokenTransactionSignablePayload(payload);
28290
28260
  const ownerSignature = await this.signMessageWithKey(
@@ -28306,7 +28276,7 @@ var TokenTransactionService = class {
28306
28276
  }
28307
28277
  const payload = {
28308
28278
  finalTokenTransactionHash,
28309
- operatorIdentityPublicKey: (0, import_utils13.hexToBytes)(operator.identityPublicKey)
28279
+ operatorIdentityPublicKey: (0, import_utils14.hexToBytes)(operator.identityPublicKey)
28310
28280
  };
28311
28281
  const payloadHash = await hashOperatorSpecificTokenTransactionSignablePayload(payload);
28312
28282
  const ownerSignature = await this.signMessageWithKey(
@@ -28322,7 +28292,7 @@ var TokenTransactionService = class {
28322
28292
  for (let i = 0; i < transferInput.outputsToSpend.length; i++) {
28323
28293
  const payload = {
28324
28294
  finalTokenTransactionHash,
28325
- operatorIdentityPublicKey: (0, import_utils13.hexToBytes)(operator.identityPublicKey)
28295
+ operatorIdentityPublicKey: (0, import_utils14.hexToBytes)(operator.identityPublicKey)
28326
28296
  };
28327
28297
  const payloadHash = await hashOperatorSpecificTokenTransactionSignablePayload(payload);
28328
28298
  let ownerSignature;
@@ -28339,7 +28309,7 @@ var TokenTransactionService = class {
28339
28309
  }
28340
28310
  inputTtxoSignaturesPerOperator.push({
28341
28311
  ttxoSignatures,
28342
- operatorIdentityPublicKey: (0, import_utils13.hexToBytes)(operator.identityPublicKey)
28312
+ operatorIdentityPublicKey: (0, import_utils14.hexToBytes)(operator.identityPublicKey)
28343
28313
  });
28344
28314
  }
28345
28315
  return inputTtxoSignaturesPerOperator;
@@ -28349,22 +28319,22 @@ var TokenTransactionService = class {
28349
28319
  // src/utils/adaptor-signature.ts
28350
28320
  var import_modular = require("@noble/curves/abstract/modular");
28351
28321
  var import_secp256k17 = require("@noble/curves/secp256k1");
28352
- var import_utils14 = require("@noble/curves/utils");
28322
+ var import_utils15 = require("@noble/curves/utils");
28353
28323
  function generateSignatureFromExistingAdaptor(signature, adaptorPrivateKeyBytes) {
28354
28324
  const { r, s } = parseSignature(signature);
28355
- const sBigInt = (0, import_utils14.bytesToNumberBE)(s);
28356
- const tBigInt = (0, import_utils14.bytesToNumberBE)(adaptorPrivateKeyBytes);
28325
+ const sBigInt = (0, import_utils15.bytesToNumberBE)(s);
28326
+ const tBigInt = (0, import_utils15.bytesToNumberBE)(adaptorPrivateKeyBytes);
28357
28327
  const newS = (0, import_modular.mod)(sBigInt - tBigInt, import_secp256k17.secp256k1.CURVE.n);
28358
- const newSignature = new Uint8Array([...r, ...(0, import_utils14.numberToBytesBE)(newS, 32)]);
28328
+ const newSignature = new Uint8Array([...r, ...(0, import_utils15.numberToBytesBE)(newS, 32)]);
28359
28329
  return newSignature;
28360
28330
  }
28361
28331
  function generateAdaptorFromSignature(signature) {
28362
28332
  const adaptorPrivateKey = import_secp256k17.secp256k1.utils.randomPrivateKey();
28363
28333
  const { r, s } = parseSignature(signature);
28364
- const sBigInt = (0, import_utils14.bytesToNumberBE)(s);
28365
- const tBigInt = (0, import_utils14.bytesToNumberBE)(adaptorPrivateKey);
28334
+ const sBigInt = (0, import_utils15.bytesToNumberBE)(s);
28335
+ const tBigInt = (0, import_utils15.bytesToNumberBE)(adaptorPrivateKey);
28366
28336
  const newS = (0, import_modular.mod)(sBigInt - tBigInt, import_secp256k17.secp256k1.CURVE.n);
28367
- const newSignature = new Uint8Array([...r, ...(0, import_utils14.numberToBytesBE)(newS, 32)]);
28337
+ const newSignature = new Uint8Array([...r, ...(0, import_utils15.numberToBytesBE)(newS, 32)]);
28368
28338
  return {
28369
28339
  adaptorSignature: newSignature,
28370
28340
  adaptorPrivateKey
@@ -28381,10 +28351,10 @@ function validateOutboundAdaptorSignature(pubkey, hash, signature, adaptorPubkey
28381
28351
  }
28382
28352
  function applyAdaptorToSignature(pubkey, hash, signature, adaptorPrivateKeyBytes) {
28383
28353
  const { r, s } = parseSignature(signature);
28384
- const sBigInt = (0, import_utils14.bytesToNumberBE)(s);
28385
- const adaptorPrivateKey = (0, import_utils14.bytesToNumberBE)(adaptorPrivateKeyBytes);
28354
+ const sBigInt = (0, import_utils15.bytesToNumberBE)(s);
28355
+ const adaptorPrivateKey = (0, import_utils15.bytesToNumberBE)(adaptorPrivateKeyBytes);
28386
28356
  const newS = (0, import_modular.mod)(sBigInt + adaptorPrivateKey, import_secp256k17.secp256k1.CURVE.n);
28387
- const newSig = new Uint8Array([...r, ...(0, import_utils14.numberToBytesBE)(newS, 32)]);
28357
+ const newSig = new Uint8Array([...r, ...(0, import_utils15.numberToBytesBE)(newS, 32)]);
28388
28358
  try {
28389
28359
  if (import_secp256k17.schnorr.verify(newSig, hash, pubkey)) {
28390
28360
  return newSig;
@@ -28393,7 +28363,7 @@ function applyAdaptorToSignature(pubkey, hash, signature, adaptorPrivateKeyBytes
28393
28363
  console.error("[applyAdaptorToSignature] Addition verification failed:", e);
28394
28364
  }
28395
28365
  const altS = (0, import_modular.mod)(sBigInt - adaptorPrivateKey, import_secp256k17.secp256k1.CURVE.n);
28396
- const altSig = new Uint8Array([...r, ...(0, import_utils14.numberToBytesBE)(altS, 32)]);
28366
+ const altSig = new Uint8Array([...r, ...(0, import_utils15.numberToBytesBE)(altS, 32)]);
28397
28367
  try {
28398
28368
  if (import_secp256k17.schnorr.verify(altSig, hash, pubkey)) {
28399
28369
  return altSig;
@@ -28410,7 +28380,7 @@ function schnorrVerifyWithAdaptor(signature, hash, pubKeyBytes, adaptorPubkey, i
28410
28380
  if (hash.length !== 32) {
28411
28381
  throw new Error(`wrong size for message (got ${hash.length}, want 32)`);
28412
28382
  }
28413
- const pubKey = import_secp256k17.schnorr.utils.lift_x((0, import_utils14.bytesToNumberBE)(pubKeyBytes));
28383
+ const pubKey = import_secp256k17.schnorr.utils.lift_x((0, import_utils15.bytesToNumberBE)(pubKeyBytes));
28414
28384
  pubKey.assertValidity();
28415
28385
  const { r, s } = parseSignature(signature);
28416
28386
  const commitmenet = import_secp256k17.schnorr.utils.taggedHash(
@@ -28422,9 +28392,9 @@ function schnorrVerifyWithAdaptor(signature, hash, pubKeyBytes, adaptorPubkey, i
28422
28392
  if (commitmenet.length > 32) {
28423
28393
  throw new Error("hash of (r || P || m) too big");
28424
28394
  }
28425
- const e = (0, import_modular.mod)((0, import_utils14.bytesToNumberBE)(commitmenet), import_secp256k17.secp256k1.CURVE.n);
28395
+ const e = (0, import_modular.mod)((0, import_utils15.bytesToNumberBE)(commitmenet), import_secp256k17.secp256k1.CURVE.n);
28426
28396
  const negE = (0, import_modular.mod)(-e, import_secp256k17.secp256k1.CURVE.n);
28427
- const sG = import_secp256k17.secp256k1.Point.BASE.multiplyUnsafe((0, import_utils14.bytesToNumberBE)(s));
28397
+ const sG = import_secp256k17.secp256k1.Point.BASE.multiplyUnsafe((0, import_utils15.bytesToNumberBE)(s));
28428
28398
  const eP = pubKey.multiplyUnsafe(negE);
28429
28399
  const R = sG.add(eP);
28430
28400
  if (R.is0()) {
@@ -28440,7 +28410,7 @@ function schnorrVerifyWithAdaptor(signature, hash, pubKeyBytes, adaptorPubkey, i
28440
28410
  if (newR.y % 2n !== 0n) {
28441
28411
  throw new Error("calculated R y-value is odd");
28442
28412
  }
28443
- const rNum = (0, import_utils14.bytesToNumberBE)(r);
28413
+ const rNum = (0, import_utils15.bytesToNumberBE)(r);
28444
28414
  if (newR.toAffine().x !== rNum) {
28445
28415
  throw new Error("calculated R point was not given R");
28446
28416
  }
@@ -28461,15 +28431,15 @@ function parseSignature(signature) {
28461
28431
  }
28462
28432
  const r = signature.slice(0, 32);
28463
28433
  const s = signature.slice(32, 64);
28464
- if ((0, import_utils14.bytesToNumberBE)(r) >= import_secp256k17.secp256k1.CURVE.Fp.ORDER) {
28434
+ if ((0, import_utils15.bytesToNumberBE)(r) >= import_secp256k17.secp256k1.CURVE.Fp.ORDER) {
28465
28435
  throw new ValidationError("Invalid signature: r >= field prime", {
28466
- rValue: (0, import_utils14.bytesToNumberBE)(r),
28436
+ rValue: (0, import_utils15.bytesToNumberBE)(r),
28467
28437
  fieldPrime: import_secp256k17.secp256k1.CURVE.Fp.ORDER
28468
28438
  });
28469
28439
  }
28470
- if ((0, import_utils14.bytesToNumberBE)(s) >= import_secp256k17.secp256k1.CURVE.n) {
28440
+ if ((0, import_utils15.bytesToNumberBE)(s) >= import_secp256k17.secp256k1.CURVE.n) {
28471
28441
  throw new ValidationError("Invalid signature: s >= group order", {
28472
- sValue: (0, import_utils14.bytesToNumberBE)(s),
28442
+ sValue: (0, import_utils15.bytesToNumberBE)(s),
28473
28443
  groupOrder: import_secp256k17.secp256k1.CURVE.n
28474
28444
  });
28475
28445
  }
@@ -28492,7 +28462,7 @@ var isWebExtension = (
28492
28462
  "chrome" in globalThis && globalThis.chrome.runtime?.id
28493
28463
  );
28494
28464
  var userAgent = "navigator" in globalThis ? globalThis.navigator.userAgent || "unknown-user-agent" : void 0;
28495
- var packageVersion = true ? "0.3.8" : "unknown";
28465
+ var packageVersion = true ? "0.4.0" : "unknown";
28496
28466
  var baseEnvStr = "unknown";
28497
28467
  if (isBun) {
28498
28468
  const bunVersion = "version" in globalThis.Bun ? globalThis.Bun.version : "unknown-version";
@@ -28513,13 +28483,10 @@ if (isBun) {
28513
28483
  }
28514
28484
  var clientEnv = `js-spark-sdk/${packageVersion} ${baseEnvStr}`;
28515
28485
 
28516
- // src/services/signing.ts
28517
- var import_utils16 = require("@noble/curves/utils");
28518
-
28519
28486
  // src/utils/htlc-transactions.ts
28520
- var import_btc_signer5 = require("@scure/btc-signer");
28487
+ var import_btc_signer3 = require("@scure/btc-signer");
28521
28488
  var import_secp256k18 = require("@noble/curves/secp256k1");
28522
- var import_utils15 = require("@noble/curves/utils");
28489
+ var import_utils16 = require("@noble/curves/utils");
28523
28490
  var PUB_KEY_BYTES = "0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0";
28524
28491
  function numsPoint() {
28525
28492
  const withdrawalPubKeyPoint = import_secp256k18.secp256k1.Point.fromHex(PUB_KEY_BYTES);
@@ -28597,10 +28564,10 @@ function createLightningHTLCTransaction({
28597
28564
  outAmount = maybeApplyFee(outAmount);
28598
28565
  }
28599
28566
  const input = {
28600
- txid: (0, import_utils15.hexToBytes)(getTxId(nodeTx)),
28567
+ txid: (0, import_utils16.hexToBytes)(getTxId(nodeTx)),
28601
28568
  index: 0
28602
28569
  };
28603
- const htlcTransaction = new import_btc_signer5.Transaction({
28570
+ const htlcTransaction = new import_btc_signer3.Transaction({
28604
28571
  version: 3,
28605
28572
  allowUnknownOutputs: true
28606
28573
  });
@@ -28639,12 +28606,12 @@ function createHTLCTaprootAddress({
28639
28606
  );
28640
28607
  const hashLockLeaf = { leafVersion: 192, script: hashLockScript };
28641
28608
  const sequenceLockLeaf = { leafVersion: 192, script: sequenceLockScript };
28642
- const scriptTree = (0, import_btc_signer5.taprootListToTree)([hashLockLeaf, sequenceLockLeaf]);
28643
- const p2trScript = (0, import_btc_signer5.p2tr)(numsKey, scriptTree, network, true).script;
28609
+ const scriptTree = (0, import_btc_signer3.taprootListToTree)([hashLockLeaf, sequenceLockLeaf]);
28610
+ const p2trScript = (0, import_btc_signer3.p2tr)(numsKey, scriptTree, network, true).script;
28644
28611
  return p2trScript;
28645
28612
  }
28646
28613
  function createHashLockScript(hash, pubkey) {
28647
- const result = import_btc_signer5.Script.encode([
28614
+ const result = import_btc_signer3.Script.encode([
28648
28615
  "SHA256",
28649
28616
  hash,
28650
28617
  "EQUALVERIFY",
@@ -28654,8 +28621,8 @@ function createHashLockScript(hash, pubkey) {
28654
28621
  return result;
28655
28622
  }
28656
28623
  function createSequenceLockScript(sequence, sequenceLockDestinationPubkey) {
28657
- const result = import_btc_signer5.Script.encode([
28658
- (0, import_btc_signer5.ScriptNum)().encode(BigInt(sequence)),
28624
+ const result = import_btc_signer3.Script.encode([
28625
+ (0, import_btc_signer3.ScriptNum)().encode(BigInt(sequence)),
28659
28626
  "CHECKSEQUENCEVERIFY",
28660
28627
  "DROP",
28661
28628
  sequenceLockDestinationPubkey.slice(1, 33),
@@ -28719,20 +28686,7 @@ var SigningService = class {
28719
28686
  });
28720
28687
  }
28721
28688
  const nodeTx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
28722
- const cpfpNodeOutPoint = {
28723
- txid: (0, import_utils16.hexToBytes)(getTxId(nodeTx)),
28724
- index: 0
28725
- };
28726
28689
  const currRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
28727
- const sequence = currRefundTx.getInput(0).sequence;
28728
- if (!sequence) {
28729
- throw new ValidationError("Invalid refund transaction", {
28730
- field: "sequence",
28731
- value: currRefundTx.getInput(0),
28732
- expected: "Non-null sequence"
28733
- });
28734
- }
28735
- const { nextSequence, nextDirectSequence } = getNextTransactionSequence(sequence);
28736
28690
  const amountSats = currRefundTx.getOutput(0).amount;
28737
28691
  if (amountSats === void 0) {
28738
28692
  throw new ValidationError("Invalid refund transaction", {
@@ -28742,20 +28696,21 @@ var SigningService = class {
28742
28696
  });
28743
28697
  }
28744
28698
  let directNodeTx;
28745
- let directNodeOutPoint;
28746
28699
  if (leaf.leaf.directTx.length > 0) {
28747
28700
  directNodeTx = getTxFromRawTxBytes(leaf.leaf.directTx);
28748
- directNodeOutPoint = {
28749
- txid: (0, import_utils16.hexToBytes)(getTxId(directNodeTx)),
28750
- index: 0
28751
- };
28752
28701
  }
28753
- const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
28754
- sequence: nextSequence,
28755
- directSequence: nextDirectSequence,
28756
- input: cpfpNodeOutPoint,
28757
- directInput: directNodeOutPoint,
28758
- amountSats,
28702
+ const currentSequence = currRefundTx.getInput(0).sequence;
28703
+ if (!currentSequence) {
28704
+ throw new ValidationError("Invalid refund transaction", {
28705
+ field: "sequence",
28706
+ value: currRefundTx.getInput(0),
28707
+ expected: "Non-null sequence"
28708
+ });
28709
+ }
28710
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createDecrementedTimelockRefundTxs({
28711
+ nodeTx,
28712
+ directNodeTx,
28713
+ sequence: currentSequence,
28759
28714
  receivingPubkey: receiverIdentityPubkey,
28760
28715
  network: this.config.getNetwork()
28761
28716
  });
@@ -28771,7 +28726,8 @@ var SigningService = class {
28771
28726
  cpfpSigningCommitments[i]?.signingNonceCommitments
28772
28727
  );
28773
28728
  cpfpLeafSigningJobs.push(...signingJobs);
28774
- if (directRefundTx) {
28729
+ const isZeroNode = getCurrentTimelock(nodeTx.getInput(0).sequence);
28730
+ if (directRefundTx && !isZeroNode) {
28775
28731
  if (!directNodeTx) {
28776
28732
  throw new ValidationError(
28777
28733
  "Direct node transaction undefined while direct refund transaction is defined",
@@ -28796,16 +28752,6 @@ var SigningService = class {
28796
28752
  directLeafSigningJobs.push(...signingJobs2);
28797
28753
  }
28798
28754
  if (directFromCpfpRefundTx) {
28799
- if (!directNodeTx) {
28800
- throw new ValidationError(
28801
- "Direct node transaction undefined while direct from CPFP refund transaction is defined",
28802
- {
28803
- field: "directNodeTx",
28804
- value: directNodeTx,
28805
- expected: "Non-null direct node transaction"
28806
- }
28807
- );
28808
- }
28809
28755
  const refundSighash2 = getSigHashFromTx(
28810
28756
  directFromCpfpRefundTx,
28811
28757
  0,
@@ -28932,6 +28878,35 @@ var SigningService = class {
28932
28878
  directFromCpfpLeafSigningJobs
28933
28879
  };
28934
28880
  }
28881
+ async signSigningJobs(signingJobs) {
28882
+ const userSignedTxSigningJobs = /* @__PURE__ */ new Map();
28883
+ for (const signingJob of signingJobs) {
28884
+ const rawTx = getTxFromRawTxBytes(signingJob.rawTx);
28885
+ const txOut = signingJob.parentTxOut;
28886
+ const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
28887
+ const userSignature = await this.config.signer.signFrost({
28888
+ message: rawTxSighash,
28889
+ keyDerivation: signingJob.keyDerivation,
28890
+ publicKey: signingJob.signingPublicKey,
28891
+ verifyingKey: signingJob.verifyingKey,
28892
+ selfCommitment: signingJob.signingNonceCommitment,
28893
+ statechainCommitments: signingJob.signingNonceCommitments,
28894
+ adaptorPubKey: new Uint8Array()
28895
+ });
28896
+ const userSignedTxSigningJob = {
28897
+ leafId: signingJob.leafId,
28898
+ signingPublicKey: signingJob.signingPublicKey,
28899
+ rawTx: rawTx.toBytes(),
28900
+ signingNonceCommitment: signingJob.signingNonceCommitment.commitment,
28901
+ signingCommitments: {
28902
+ signingCommitments: signingJob.signingNonceCommitments
28903
+ },
28904
+ userSignature
28905
+ };
28906
+ userSignedTxSigningJobs.set(signingJob.type, userSignedTxSigningJob);
28907
+ }
28908
+ return userSignedTxSigningJobs;
28909
+ }
28935
28910
  };
28936
28911
 
28937
28912
  // src/signer/signer.ts
@@ -29731,7 +29706,7 @@ var TaprootSparkSigner = class extends DefaultSparkSigner {
29731
29706
  var import_secp256k113 = require("@noble/curves/secp256k1");
29732
29707
  var import_utils21 = require("@noble/curves/utils");
29733
29708
  var btc3 = __toESM(require("@scure/btc-signer"), 1);
29734
- var import_btc_signer6 = require("@scure/btc-signer");
29709
+ var import_btc_signer4 = require("@scure/btc-signer");
29735
29710
  var import_utils22 = require("@scure/btc-signer/utils");
29736
29711
  var STATIC_FAUCET_KEY = (0, import_utils21.hexToBytes)(
29737
29712
  "deadbeef1337cafe4242424242424242deadbeef1337cafe4242424242424242"
@@ -29819,7 +29794,7 @@ var BitcoinFaucet = class _BitcoinFaucet {
29819
29794
  );
29820
29795
  await this.generateToAddress(1, address);
29821
29796
  const fundingTxRaw = await this.getRawTransaction(fundingTxid);
29822
- const fundingTx = import_btc_signer6.Transaction.fromRaw((0, import_utils21.hexToBytes)(fundingTxRaw.hex));
29797
+ const fundingTx = import_btc_signer4.Transaction.fromRaw((0, import_utils21.hexToBytes)(fundingTxRaw.hex));
29823
29798
  for (let i = 0; i < fundingTx.outputsLength; i++) {
29824
29799
  const output = fundingTx.getOutput(i);
29825
29800
  if (!output.script || !output.amount) continue;
@@ -29850,7 +29825,7 @@ var BitcoinFaucet = class _BitcoinFaucet {
29850
29825
  `Selected UTXO (${selectedUtxoAmountSats} sats) is too small to create even one faucet coin of ${COIN_AMOUNT} sats`
29851
29826
  );
29852
29827
  }
29853
- const splitTx = new import_btc_signer6.Transaction();
29828
+ const splitTx = new import_btc_signer4.Transaction();
29854
29829
  splitTx.addInput({
29855
29830
  txid: selectedUtxo.txid,
29856
29831
  index: selectedUtxo.vout
@@ -29893,7 +29868,7 @@ var BitcoinFaucet = class _BitcoinFaucet {
29893
29868
  }
29894
29869
  }
29895
29870
  async sendFaucetCoinToP2WPKHAddress(pubKey) {
29896
- const sendToPubKeyTx = new import_btc_signer6.Transaction();
29871
+ const sendToPubKeyTx = new import_btc_signer4.Transaction();
29897
29872
  const p2wpkhAddress = btc3.p2wpkh(pubKey, getNetwork(4 /* LOCAL */)).address;
29898
29873
  if (!p2wpkhAddress) {
29899
29874
  throw new Error("Invalid P2WPKH address");
@@ -29929,7 +29904,7 @@ var BitcoinFaucet = class _BitcoinFaucet {
29929
29904
  const sighash = unsignedTx.preimageWitnessV1(
29930
29905
  0,
29931
29906
  new Array(unsignedTx.inputsLength).fill(script),
29932
- import_btc_signer6.SigHash.DEFAULT,
29907
+ import_btc_signer4.SigHash.DEFAULT,
29933
29908
  new Array(unsignedTx.inputsLength).fill(fundingTxOut.amount)
29934
29909
  );
29935
29910
  const merkleRoot = new Uint8Array();
@@ -29948,6 +29923,14 @@ var BitcoinFaucet = class _BitcoinFaucet {
29948
29923
  async mineBlocks(numBlocks) {
29949
29924
  return await this.generateToAddress(numBlocks, this.miningAddress);
29950
29925
  }
29926
+ async mineBlocksAndWaitForMiningToComplete(numBlocks) {
29927
+ const startBlock = await this.getBlockCount();
29928
+ await this.mineBlocks(numBlocks);
29929
+ await this.waitForBlocksMined({
29930
+ startBlock,
29931
+ expectedIncrease: numBlocks
29932
+ });
29933
+ }
29951
29934
  async call(method, params) {
29952
29935
  try {
29953
29936
  const { fetch, Headers: Headers3 } = getFetch();
@@ -30000,27 +29983,62 @@ var BitcoinFaucet = class _BitcoinFaucet {
30000
29983
  async getBlock(blockHash) {
30001
29984
  return await this.call("getblock", [blockHash, 2]);
30002
29985
  }
29986
+ async getBlockCount() {
29987
+ return await this.call("getblockcount", []);
29988
+ }
29989
+ async waitForBlocksMined({
29990
+ startBlock,
29991
+ expectedIncrease,
29992
+ timeoutMs = 3e4,
29993
+ intervalMs = 5e3
29994
+ }) {
29995
+ const deadline = Date.now() + timeoutMs;
29996
+ await new Promise((r) => setTimeout(r, intervalMs));
29997
+ const start = startBlock;
29998
+ const target = start + expectedIncrease;
29999
+ while (Date.now() < deadline) {
30000
+ const currentBlock = await this.getBlockCount();
30001
+ if (currentBlock >= target) return currentBlock;
30002
+ await new Promise((r) => setTimeout(r, intervalMs));
30003
+ }
30004
+ throw new Error(
30005
+ `Timed out waiting for ${expectedIncrease} blocks (target height ${target})`
30006
+ );
30007
+ }
30003
30008
  async broadcastTx(txHex) {
30004
30009
  let response = await this.call("sendrawtransaction", [txHex, 0]);
30005
30010
  return response;
30006
30011
  }
30012
+ async submitPackage(txHexs) {
30013
+ let response = await this.call("submitpackage", [txHexs]);
30014
+ return response;
30015
+ }
30007
30016
  async getNewAddress() {
30008
30017
  const key = import_secp256k113.secp256k1.utils.randomPrivateKey();
30009
30018
  const pubKey = import_secp256k113.secp256k1.getPublicKey(key);
30010
30019
  return getP2TRAddressFromPublicKey(pubKey, 4 /* LOCAL */);
30011
30020
  }
30021
+ async getNewExternalWallet() {
30022
+ const key = import_secp256k113.secp256k1.utils.randomPrivateKey();
30023
+ const pubKey = import_secp256k113.secp256k1.getPublicKey(key);
30024
+ return {
30025
+ address: getP2TRAddressFromPublicKey(pubKey, 4 /* LOCAL */),
30026
+ key,
30027
+ pubKey
30028
+ };
30029
+ }
30012
30030
  async sendToAddress(address, amount, blocksToGenerate = 1) {
30013
30031
  const coin = await this.fund();
30014
30032
  if (!coin) {
30015
30033
  throw new Error("No coins available");
30016
30034
  }
30017
- const tx = new import_btc_signer6.Transaction();
30035
+ const tx = new import_btc_signer4.Transaction();
30018
30036
  tx.addInput(coin.outpoint);
30019
30037
  const availableAmount = COIN_AMOUNT - FEE_AMOUNT;
30020
- const destinationAddress = (0, import_btc_signer6.Address)(getNetwork(4 /* LOCAL */)).decode(
30038
+ const destinationAddress = (0, import_btc_signer4.Address)(getNetwork(4 /* LOCAL */)).decode(
30021
30039
  address
30022
30040
  );
30023
- const destinationScript = import_btc_signer6.OutScript.encode(destinationAddress);
30041
+ const destinationScript = import_btc_signer4.OutScript.encode(destinationAddress);
30024
30042
  tx.addOutput({
30025
30043
  script: destinationScript,
30026
30044
  amount
@@ -30109,75 +30127,6 @@ function chunkArray(arr, size) {
30109
30127
  return chunks;
30110
30128
  }
30111
30129
 
30112
- // src/utils/optimize.ts
30113
- var DENOMINATIONS = Array.from({ length: 28 }, (_, i) => 2 ** i);
30114
- function assert(condition, message) {
30115
- if (!condition) {
30116
- throw new InternalValidationError(message || "Assertion failed");
30117
- }
30118
- }
30119
- function sum(arr) {
30120
- return arr.reduce((a, b) => a + b, 0);
30121
- }
30122
- function sorted(arr) {
30123
- return [...arr].sort((a, b) => a - b);
30124
- }
30125
- function equals(a, b) {
30126
- return a.length === b.length && a.every((val, index) => val === b[index]);
30127
- }
30128
- function greedyLeaves(amount) {
30129
- const leaves = [];
30130
- let remaining = amount;
30131
- for (let i = DENOMINATIONS.length - 1; i >= 0; i--) {
30132
- const leaf = DENOMINATIONS[i];
30133
- if (typeof leaf === "number" && leaf > 0) {
30134
- while (remaining >= leaf) {
30135
- remaining -= leaf;
30136
- leaves.push(leaf);
30137
- }
30138
- }
30139
- }
30140
- assert(sum(leaves) === amount, "greedy_leaves: sum mismatch");
30141
- return sorted(leaves);
30142
- }
30143
- var Swap = class {
30144
- inLeaves;
30145
- outLeaves;
30146
- constructor(inLeaves, outLeaves) {
30147
- this.inLeaves = [...inLeaves];
30148
- this.outLeaves = [...outLeaves];
30149
- assert(
30150
- sum(this.inLeaves) === sum(this.outLeaves),
30151
- "Swap in/out leaves must sum to same value for swap: " + this.toString()
30152
- );
30153
- }
30154
- toString() {
30155
- return `Swap(in=${JSON.stringify(this.inLeaves)}, out=${JSON.stringify(this.outLeaves)})`;
30156
- }
30157
- };
30158
- function maximizeUnilateralExit(inputLeaves, maxLeavesPerSwap = 64) {
30159
- const swaps = [];
30160
- let batch = [];
30161
- let leaves = sorted(inputLeaves);
30162
- while (leaves.length > 0) {
30163
- batch.push(leaves.shift());
30164
- const target = greedyLeaves(sum(batch));
30165
- if (batch.length >= maxLeavesPerSwap || target.length >= maxLeavesPerSwap) {
30166
- if (!equals(target, batch)) {
30167
- swaps.push(new Swap([...batch], target));
30168
- }
30169
- batch = [];
30170
- }
30171
- }
30172
- if (batch.length > 0) {
30173
- const target = greedyLeaves(sum(batch));
30174
- if (!equals(target, batch)) {
30175
- swaps.push(new Swap([...batch], target));
30176
- }
30177
- }
30178
- return swaps;
30179
- }
30180
-
30181
30130
  // src/utils/retry.ts
30182
30131
  var DEFAULT_RETRY_CONFIG = {
30183
30132
  maxAttempts: 5,
@@ -30254,6 +30203,199 @@ var SparkWalletEvent = {
30254
30203
  StreamReconnecting: "stream:reconnecting"
30255
30204
  };
30256
30205
 
30206
+ // src/utils/optimize.ts
30207
+ var DENOMINATIONS = Array.from({ length: 28 }, (_, i) => 2 ** i);
30208
+ function assert(condition, message) {
30209
+ if (!condition) {
30210
+ throw new InternalValidationError(message || "Assertion failed");
30211
+ }
30212
+ }
30213
+ function sum(arr) {
30214
+ return arr.reduce((a, b) => a + b, 0);
30215
+ }
30216
+ function sorted(arr) {
30217
+ return [...arr].sort((a, b) => a - b);
30218
+ }
30219
+ function equals(a, b) {
30220
+ return a.length === b.length && a.every((val, index) => val === b[index]);
30221
+ }
30222
+ function countOccurrences(arr) {
30223
+ const map = /* @__PURE__ */ new Map();
30224
+ for (const x of arr) {
30225
+ map.set(x, (map.get(x) ?? 0) + 1);
30226
+ }
30227
+ return map;
30228
+ }
30229
+ function subtractCounters(a, b) {
30230
+ const result = /* @__PURE__ */ new Map();
30231
+ for (const [key, value] of a.entries()) {
30232
+ const diff = value - (b.get(key) ?? 0);
30233
+ if (diff > 0) {
30234
+ result.set(key, diff);
30235
+ }
30236
+ }
30237
+ return result;
30238
+ }
30239
+ function counterToFlatArray(counter) {
30240
+ const arr = [];
30241
+ for (const [k, v] of Array.from(counter.entries()).sort(
30242
+ (a, b) => a[0] - b[0]
30243
+ )) {
30244
+ for (let i = 0; i < v; i++) {
30245
+ arr.push(k);
30246
+ }
30247
+ }
30248
+ return arr;
30249
+ }
30250
+ function greedyLeaves(amount) {
30251
+ const leaves = [];
30252
+ let remaining = amount;
30253
+ for (let i = DENOMINATIONS.length - 1; i >= 0; i--) {
30254
+ const leaf = DENOMINATIONS[i];
30255
+ if (typeof leaf === "number" && leaf > 0) {
30256
+ while (remaining >= leaf) {
30257
+ remaining -= leaf;
30258
+ leaves.push(leaf);
30259
+ }
30260
+ }
30261
+ }
30262
+ assert(sum(leaves) === amount, "greedy_leaves: sum mismatch");
30263
+ return sorted(leaves);
30264
+ }
30265
+ function swapMinimizingLeaves(amount, multiplicity = 1) {
30266
+ const leaves = [];
30267
+ let remaining = amount;
30268
+ assert(multiplicity > 0, "multiplicity must be > 0");
30269
+ for (const leaf of DENOMINATIONS) {
30270
+ if (typeof leaf === "number" && leaf > 0) {
30271
+ for (let i = 0; i < multiplicity; i++) {
30272
+ if (remaining >= leaf) {
30273
+ remaining -= leaf;
30274
+ leaves.push(leaf);
30275
+ }
30276
+ }
30277
+ }
30278
+ }
30279
+ leaves.push(...greedyLeaves(remaining));
30280
+ assert(sum(leaves) === amount, "swap_minimizing_leaves: sum mismatch");
30281
+ return sorted(leaves);
30282
+ }
30283
+ var Swap = class {
30284
+ inLeaves;
30285
+ outLeaves;
30286
+ constructor(inLeaves, outLeaves) {
30287
+ this.inLeaves = [...inLeaves];
30288
+ this.outLeaves = [...outLeaves];
30289
+ assert(
30290
+ sum(this.inLeaves) === sum(this.outLeaves),
30291
+ "Swap in/out leaves must sum to same value for swap: " + this.toString()
30292
+ );
30293
+ }
30294
+ toString() {
30295
+ return `Swap(in=${JSON.stringify(this.inLeaves)}, out=${JSON.stringify(this.outLeaves)})`;
30296
+ }
30297
+ };
30298
+ function maximizeUnilateralExit(inputLeaves, maxLeavesPerSwap = 64) {
30299
+ const swaps = [];
30300
+ let batch = [];
30301
+ let leaves = sorted(inputLeaves);
30302
+ while (leaves.length > 0) {
30303
+ batch.push(leaves.shift());
30304
+ const target = greedyLeaves(sum(batch));
30305
+ if (batch.length >= maxLeavesPerSwap || target.length >= maxLeavesPerSwap) {
30306
+ if (!equals(target, batch)) {
30307
+ swaps.push(new Swap([...batch], target));
30308
+ }
30309
+ batch = [];
30310
+ }
30311
+ }
30312
+ if (batch.length > 0) {
30313
+ const target = greedyLeaves(sum(batch));
30314
+ if (!equals(target, batch)) {
30315
+ swaps.push(new Swap([...batch], target));
30316
+ }
30317
+ }
30318
+ return swaps;
30319
+ }
30320
+ function minimizeTransferSwap(inputLeaves, multiplicity = 1, maxLeavesPerSwap = 64) {
30321
+ const balance = sum(inputLeaves);
30322
+ const optimalLeaves = swapMinimizingLeaves(balance, multiplicity);
30323
+ const walletCounter = countOccurrences(inputLeaves);
30324
+ const optimalCounter = countOccurrences(optimalLeaves);
30325
+ const leavesToGive = subtractCounters(walletCounter, optimalCounter);
30326
+ const leavesToReceive = subtractCounters(optimalCounter, walletCounter);
30327
+ const leavesToGiveFlat = counterToFlatArray(leavesToGive);
30328
+ const leavesToReceiveFlat = counterToFlatArray(leavesToReceive);
30329
+ const swaps = [];
30330
+ let toGiveBatch = [];
30331
+ let toReceiveBatch = [];
30332
+ let give = [...leavesToGiveFlat];
30333
+ let receive = [...leavesToReceiveFlat];
30334
+ while (give.length > 0 || receive.length > 0) {
30335
+ if (sum(toGiveBatch) > sum(toReceiveBatch)) {
30336
+ if (receive.length === 0) break;
30337
+ toReceiveBatch.push(receive.shift());
30338
+ } else {
30339
+ if (give.length === 0) break;
30340
+ toGiveBatch.push(give.shift());
30341
+ }
30342
+ if (toGiveBatch.length > 0 && toReceiveBatch.length > 0 && sum(toGiveBatch) === sum(toReceiveBatch)) {
30343
+ if (toGiveBatch.length > maxLeavesPerSwap) {
30344
+ for (let i = 0; i < toGiveBatch.length; i += maxLeavesPerSwap) {
30345
+ const subset = toGiveBatch.slice(i, i + maxLeavesPerSwap);
30346
+ swaps.push(new Swap(subset, greedyLeaves(sum(subset))));
30347
+ }
30348
+ } else if (toReceiveBatch.length > maxLeavesPerSwap) {
30349
+ for (let cutoff = maxLeavesPerSwap; cutoff > 0; cutoff--) {
30350
+ const sumCut = sum(toReceiveBatch.slice(0, cutoff));
30351
+ const remainder = sum(toGiveBatch) - sumCut;
30352
+ const alternateBatch = [
30353
+ ...toReceiveBatch.slice(0, cutoff),
30354
+ ...greedyLeaves(remainder)
30355
+ ];
30356
+ if (alternateBatch.length <= maxLeavesPerSwap) {
30357
+ swaps.push(new Swap([...toGiveBatch], alternateBatch));
30358
+ break;
30359
+ }
30360
+ }
30361
+ } else {
30362
+ swaps.push(new Swap([...toGiveBatch], [...toReceiveBatch]));
30363
+ }
30364
+ toGiveBatch = [];
30365
+ toReceiveBatch = [];
30366
+ }
30367
+ }
30368
+ return swaps;
30369
+ }
30370
+ function shouldOptimize(inputLeaves, multiplicity = 1, maxLeavesPerSwap = 64) {
30371
+ if (multiplicity == 0) {
30372
+ const swaps = maximizeUnilateralExit(inputLeaves, maxLeavesPerSwap);
30373
+ const numInputs = sum(swaps.map((swap) => swap.inLeaves.length));
30374
+ const numOutputs = sum(swaps.map((swap) => swap.outLeaves.length));
30375
+ return numOutputs * 5 < numInputs;
30376
+ } else {
30377
+ const swaps = minimizeTransferSwap(
30378
+ inputLeaves,
30379
+ multiplicity,
30380
+ maxLeavesPerSwap
30381
+ );
30382
+ const inputCounter = countOccurrences(
30383
+ swaps.flatMap((swap) => swap.inLeaves)
30384
+ );
30385
+ const outputCounter = countOccurrences(
30386
+ swaps.flatMap((swap) => swap.outLeaves)
30387
+ );
30388
+ return Math.abs(inputCounter.size - outputCounter.size) > 1;
30389
+ }
30390
+ }
30391
+ function optimize(inputLeaves, multiplicity = 1, maxLeavesPerSwap = 64) {
30392
+ if (multiplicity == 0) {
30393
+ return maximizeUnilateralExit(inputLeaves, maxLeavesPerSwap);
30394
+ } else {
30395
+ return minimizeTransferSwap(inputLeaves, multiplicity, maxLeavesPerSwap);
30396
+ }
30397
+ }
30398
+
30257
30399
  // src/spark-wallet/spark-wallet.ts
30258
30400
  var SparkWallet = class extends import_eventemitter3.EventEmitter {
30259
30401
  config;
@@ -30358,16 +30500,13 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
30358
30500
  if (event.transfer.transfer && !(0, import_utils24.equalBytes)(senderIdentityPublicKey, receiverIdentityPublicKey)) {
30359
30501
  await this.claimTransfer({
30360
30502
  transfer: event.transfer.transfer,
30361
- emit: true,
30362
- optimize: true
30503
+ emit: true
30363
30504
  });
30364
30505
  }
30365
30506
  } else if (isDepositStreamEvent(event)) {
30366
30507
  const deposit = event.deposit.deposit;
30367
- const newLeaf = await this.transferService.extendTimelock(deposit);
30368
- await this.transferLeavesToSelf(newLeaf.nodes, {
30369
- type: "leaf" /* LEAF */,
30370
- path: deposit.id
30508
+ await this.withLeaves(async () => {
30509
+ this.leaves.push(deposit);
30371
30510
  });
30372
30511
  this.emit(
30373
30512
  SparkWalletEvent.DepositConfirmed,
@@ -30543,19 +30682,6 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
30543
30682
  }
30544
30683
  return availableLeaves.filter(([_, node]) => !leavesToIgnore.has(node.id)).map(([_, node]) => node);
30545
30684
  }
30546
- async checkExtendLeaves(leaves) {
30547
- await this.withLeaves(async () => {
30548
- for (const leaf of leaves) {
30549
- if (!leaf.parentNodeId && leaf.status === "AVAILABLE") {
30550
- const res = await this.transferService.extendTimelock(leaf);
30551
- await this.transferLeavesToSelf(res.nodes, {
30552
- type: "leaf" /* LEAF */,
30553
- path: leaf.id
30554
- });
30555
- }
30556
- }
30557
- });
30558
- }
30559
30685
  verifyKey(pubkey1, pubkey2, verifyingKey) {
30560
30686
  return (0, import_utils24.equalBytes)(addPublicKeys(pubkey1, pubkey2), verifyingKey);
30561
30687
  }
@@ -30665,75 +30791,84 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
30665
30791
  }
30666
30792
  return nodes;
30667
30793
  }
30668
- areLeavesInefficient() {
30669
- const totalAmount = this.getInternalBalance();
30670
- if (this.leaves.length <= 1) {
30671
- return false;
30794
+ async *optimizeLeaves(multiplicity = void 0) {
30795
+ const multiplicityValue = multiplicity ?? this.config.getOptimizationOptions().multiplicity ?? 0;
30796
+ if (multiplicityValue < 0) {
30797
+ throw new ValidationError("Multiplicity cannot be negative");
30798
+ } else if (multiplicityValue > 5) {
30799
+ throw new ValidationError("Multiplicity cannot be greater than 5");
30672
30800
  }
30673
- const nextLowerPowerOfTwo = 31 - Math.clz32(totalAmount);
30674
- let remainingAmount = totalAmount;
30675
- let optimalLeavesLength = 0;
30676
- for (let i = nextLowerPowerOfTwo; i >= 0; i--) {
30677
- const denomination = 2 ** i;
30678
- while (remainingAmount >= denomination) {
30679
- remainingAmount -= denomination;
30680
- optimalLeavesLength++;
30681
- }
30682
- }
30683
- return this.leaves.length > optimalLeavesLength * 5;
30684
- }
30685
- async optimizeLeaves() {
30686
- if (this.optimizationInProgress || !this.areLeavesInefficient()) {
30801
+ if (this.optimizationInProgress || !shouldOptimize(
30802
+ this.leaves.map((leaf) => leaf.value),
30803
+ multiplicityValue
30804
+ )) {
30687
30805
  return;
30688
30806
  }
30689
- await this.withLeaves(async () => {
30807
+ const controller = new AbortController();
30808
+ const release = await this.leavesMutex.acquire();
30809
+ try {
30690
30810
  this.optimizationInProgress = true;
30691
- try {
30692
- this.leaves = await this.getLeaves();
30693
- const swaps = maximizeUnilateralExit(
30694
- this.leaves.map((leaf) => leaf.value)
30695
- );
30696
- const valueToNodes = /* @__PURE__ */ new Map();
30697
- this.leaves.forEach((leaf) => {
30698
- if (!valueToNodes.has(leaf.value)) {
30699
- valueToNodes.set(leaf.value, []);
30700
- }
30701
- valueToNodes.get(leaf.value).push(leaf);
30702
- });
30703
- for (const swap of swaps) {
30704
- const leavesToSend = [];
30705
- for (const leafValue of swap.inLeaves) {
30706
- const nodes = valueToNodes.get(leafValue);
30707
- if (nodes && nodes.length > 0) {
30708
- const node = nodes.shift();
30709
- leavesToSend.push(node);
30710
- } else {
30711
- throw new InternalValidationError(
30712
- `No unused leaf with value ${leafValue} found in leaves`
30713
- );
30714
- }
30811
+ this.leaves = await this.getLeaves();
30812
+ const swaps = optimize(
30813
+ this.leaves.map((leaf) => leaf.value),
30814
+ multiplicityValue
30815
+ );
30816
+ if (swaps.length === 0) {
30817
+ return;
30818
+ }
30819
+ yield {
30820
+ step: 0,
30821
+ total: swaps.length,
30822
+ controller
30823
+ };
30824
+ const valueToNodes = /* @__PURE__ */ new Map();
30825
+ this.leaves.forEach((leaf) => {
30826
+ if (!valueToNodes.has(leaf.value)) {
30827
+ valueToNodes.set(leaf.value, []);
30828
+ }
30829
+ valueToNodes.get(leaf.value).push(leaf);
30830
+ });
30831
+ for (const swap of swaps) {
30832
+ if (controller.signal.aborted) {
30833
+ break;
30834
+ }
30835
+ const leavesToSend = [];
30836
+ for (const leafValue of swap.inLeaves) {
30837
+ const nodes = valueToNodes.get(leafValue);
30838
+ if (nodes && nodes.length > 0) {
30839
+ const node = nodes.shift();
30840
+ leavesToSend.push(node);
30841
+ } else {
30842
+ throw new InternalValidationError(
30843
+ `No unused leaf with value ${leafValue} found in leaves`
30844
+ );
30715
30845
  }
30716
- await this.requestLeavesSwap({
30717
- leaves: leavesToSend,
30718
- targetAmounts: swap.outLeaves
30719
- });
30720
30846
  }
30721
- this.leaves = await this.getLeaves();
30722
- } finally {
30723
- this.optimizationInProgress = false;
30847
+ await this.requestLeavesSwap({
30848
+ leaves: leavesToSend,
30849
+ targetAmounts: swap.outLeaves
30850
+ });
30851
+ yield {
30852
+ step: swaps.indexOf(swap) + 1,
30853
+ total: swaps.length,
30854
+ controller
30855
+ };
30724
30856
  }
30725
- });
30857
+ this.leaves = await this.getLeaves();
30858
+ } finally {
30859
+ this.optimizationInProgress = false;
30860
+ release();
30861
+ }
30726
30862
  }
30727
30863
  async syncWallet() {
30728
30864
  await this.syncTokenOutputs();
30729
30865
  let leaves = await this.getLeaves();
30730
- leaves = await this.checkRefreshTimelockNodes(leaves);
30731
- leaves = await this.checkExtendTimeLockNodes(leaves);
30866
+ leaves = await this.checkRenewLeaves(leaves);
30732
30867
  this.leaves = leaves;
30733
- this.checkExtendLeaves(leaves);
30734
- this.optimizeLeaves().catch((e) => {
30735
- console.error("Failed to optimize leaves", e);
30736
- });
30868
+ if (this.config.getOptimizationOptions().auto) {
30869
+ for await (const _ of this.optimizeLeaves()) {
30870
+ }
30871
+ }
30737
30872
  }
30738
30873
  async withLeaves(operation) {
30739
30874
  const release = await this.leavesMutex.acquire();
@@ -31316,8 +31451,7 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
31316
31451
  }
31317
31452
  return await this.claimTransfer({
31318
31453
  transfer: incomingTransfer,
31319
- emit: false,
31320
- optimize: false
31454
+ emit: false
31321
31455
  });
31322
31456
  } catch (e) {
31323
31457
  console.error("[processSwapBatch] Error details:", {
@@ -31711,16 +31845,16 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
31711
31845
  }
31712
31846
  );
31713
31847
  }
31714
- const tx = new import_btc_signer7.Transaction();
31848
+ const tx = new import_btc_signer5.Transaction();
31715
31849
  tx.addInput({
31716
31850
  txid: depositTransactionId,
31717
31851
  index: outputIndex,
31718
31852
  witnessScript: new Uint8Array()
31719
31853
  });
31720
- const addressDecoded = (0, import_btc_signer7.Address)(getNetwork(network)).decode(
31854
+ const addressDecoded = (0, import_btc_signer5.Address)(getNetwork(network)).decode(
31721
31855
  destinationAddress
31722
31856
  );
31723
- const outputScript = import_btc_signer7.OutScript.encode(addressDecoded);
31857
+ const outputScript = import_btc_signer5.OutScript.encode(addressDecoded);
31724
31858
  tx.addOutput({
31725
31859
  script: outputScript,
31726
31860
  amount: BigInt(creditAmountSats)
@@ -31904,8 +32038,8 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
31904
32038
  if (!output) {
31905
32039
  continue;
31906
32040
  }
31907
- const parsedScript = import_btc_signer7.OutScript.decode(output.script);
31908
- const address = (0, import_btc_signer7.Address)(getNetwork(this.config.getNetwork())).encode(
32041
+ const parsedScript = import_btc_signer5.OutScript.decode(output.script);
32042
+ const address = (0, import_btc_signer5.Address)(getNetwork(this.config.getNetwork())).encode(
31909
32043
  parsedScript
31910
32044
  );
31911
32045
  if (staticDepositAddresses.has(address)) {
@@ -31982,26 +32116,7 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
31982
32116
  depositTx,
31983
32117
  vout
31984
32118
  });
31985
- const resultingNodes = [];
31986
- for (const node of res.nodes) {
31987
- if (node.status === "AVAILABLE") {
31988
- const { nodes } = await this.transferService.extendTimelock(node);
31989
- for (const n of nodes) {
31990
- if (n.status === "AVAILABLE") {
31991
- const transfer = await this.transferLeavesToSelf([n], {
31992
- type: "leaf" /* LEAF */,
31993
- path: node.id
31994
- });
31995
- resultingNodes.push(...transfer);
31996
- } else {
31997
- resultingNodes.push(n);
31998
- }
31999
- }
32000
- } else {
32001
- resultingNodes.push(node);
32002
- }
32003
- }
32004
- return resultingNodes;
32119
+ return res.nodes;
32005
32120
  }
32006
32121
  /**
32007
32122
  * Gets all unused deposit addresses for the wallet.
@@ -32110,8 +32225,8 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
32110
32225
  if (!output) {
32111
32226
  continue;
32112
32227
  }
32113
- const parsedScript = import_btc_signer7.OutScript.decode(output.script);
32114
- const address = (0, import_btc_signer7.Address)(getNetwork(this.config.getNetwork())).encode(
32228
+ const parsedScript = import_btc_signer5.OutScript.decode(output.script);
32229
+ const address = (0, import_btc_signer5.Address)(getNetwork(this.config.getNetwork())).encode(
32115
32230
  parsedScript
32116
32231
  );
32117
32232
  if (unusedDepositAddresses.has(address)) {
@@ -32143,6 +32258,9 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
32143
32258
  depositTx,
32144
32259
  vout
32145
32260
  });
32261
+ await this.withLeaves(async () => {
32262
+ this.leaves.push(...nodes2);
32263
+ });
32146
32264
  return nodes2;
32147
32265
  });
32148
32266
  this.mutexes.delete(txid);
@@ -32171,8 +32289,8 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
32171
32289
  if (!output) {
32172
32290
  continue;
32173
32291
  }
32174
- const parsedScript = import_btc_signer7.OutScript.decode(output.script);
32175
- const address = (0, import_btc_signer7.Address)(getNetwork(this.config.getNetwork())).encode(
32292
+ const parsedScript = import_btc_signer5.OutScript.decode(output.script);
32293
+ const address = (0, import_btc_signer5.Address)(getNetwork(this.config.getNetwork())).encode(
32176
32294
  parsedScript
32177
32295
  );
32178
32296
  const unusedDepositAddress = unusedDepositAddresses.get(address);
@@ -32319,8 +32437,7 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
32319
32437
  `TreeNode group at index ${groupIndex} not found for amount ${amount} after selection`
32320
32438
  );
32321
32439
  }
32322
- let available = await this.checkRefreshTimelockNodes(group);
32323
- available = await this.checkExtendTimeLockNodes(available);
32440
+ const available = await this.checkRenewLeaves(group);
32324
32441
  if (available.length < group.length) {
32325
32442
  throw new Error(
32326
32443
  `Not enough available nodes after refresh/extend. Expected ${group.length}, got ${available.length}`
@@ -32363,7 +32480,7 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
32363
32480
  transfer.id
32364
32481
  );
32365
32482
  if (pending) {
32366
- await this.claimTransfer({ transfer: pending, optimize: true });
32483
+ await this.claimTransfer({ transfer: pending });
32367
32484
  }
32368
32485
  }
32369
32486
  return {
@@ -32413,75 +32530,45 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
32413
32530
  newKeyDerivation: { type: "random" /* RANDOM */ }
32414
32531
  };
32415
32532
  }
32416
- async checkExtendTimeLockNodes(nodes) {
32417
- const nodesToExtend = [];
32533
+ async checkRenewLeaves(nodes) {
32534
+ const nodesToRenewNode = [];
32535
+ const nodesToRenewRefund = [];
32536
+ const nodesToRenewZeroTimelock = [];
32418
32537
  const nodeIds = [];
32419
32538
  const validNodes = [];
32420
32539
  for (const node of nodes) {
32421
32540
  const nodeTx = getTxFromRawTxBytes(node.nodeTx);
32422
- const sequence = nodeTx.getInput(0).sequence;
32423
- if (!sequence) {
32541
+ const refundTx = getTxFromRawTxBytes(node.refundTx);
32542
+ const nodeSequence = nodeTx.getInput(0).sequence;
32543
+ const refundSequence = refundTx.getInput(0).sequence;
32544
+ if (nodeSequence === void 0) {
32424
32545
  throw new ValidationError("Invalid node transaction", {
32425
32546
  field: "sequence",
32426
32547
  value: nodeTx.getInput(0),
32427
32548
  expected: "Non-null sequence"
32428
32549
  });
32429
32550
  }
32430
- const needsRefresh = doesLeafNeedRefresh(sequence, true);
32431
- if (needsRefresh) {
32432
- nodesToExtend.push(node);
32433
- nodeIds.push(node.id);
32434
- } else {
32435
- validNodes.push(node);
32436
- }
32437
- }
32438
- if (nodesToExtend.length === 0) {
32439
- return validNodes;
32440
- }
32441
- const nodesToAdd = [];
32442
- for (const node of nodesToExtend) {
32443
- const { nodes: nodes2 } = await this.transferService.extendTimelock(node);
32444
- this.leaves = this.leaves.filter((leaf) => leaf.id !== node.id);
32445
- const newNodes = await this.transferLeavesToSelf(nodes2, {
32446
- type: "leaf" /* LEAF */,
32447
- path: node.id
32448
- });
32449
- nodesToAdd.push(...newNodes);
32450
- }
32451
- this.updateLeaves(nodeIds, nodesToAdd);
32452
- validNodes.push(...nodesToAdd);
32453
- return validNodes;
32454
- }
32455
- /**
32456
- * Internal method to refresh timelock nodes.
32457
- *
32458
- * @param {string} nodeId - The optional ID of the node to refresh. If not provided, all nodes will be checked.
32459
- * @returns {Promise<void>}
32460
- * @private
32461
- */
32462
- async checkRefreshTimelockNodes(nodes) {
32463
- const nodesToRefresh = [];
32464
- const nodeIds = [];
32465
- const validNodes = [];
32466
- for (const node of nodes) {
32467
- const refundTx = getTxFromRawTxBytes(node.refundTx);
32468
- const sequence = refundTx.getInput(0).sequence;
32469
- if (!sequence) {
32551
+ if (!refundSequence) {
32470
32552
  throw new ValidationError("Invalid refund transaction", {
32471
32553
  field: "sequence",
32472
32554
  value: refundTx.getInput(0),
32473
32555
  expected: "Non-null sequence"
32474
32556
  });
32475
32557
  }
32476
- const needsRefresh = doesLeafNeedRefresh(sequence);
32477
- if (needsRefresh) {
32478
- nodesToRefresh.push(node);
32558
+ if (doesTxnNeedRenewed(refundSequence)) {
32559
+ if (isZeroTimelock(nodeSequence)) {
32560
+ nodesToRenewZeroTimelock.push(node);
32561
+ } else if (doesTxnNeedRenewed(nodeSequence)) {
32562
+ nodesToRenewNode.push(node);
32563
+ } else {
32564
+ nodesToRenewRefund.push(node);
32565
+ }
32479
32566
  nodeIds.push(node.id);
32480
32567
  } else {
32481
32568
  validNodes.push(node);
32482
32569
  }
32483
32570
  }
32484
- if (nodesToRefresh.length === 0) {
32571
+ if (nodesToRenewNode.length === 0 && nodesToRenewRefund.length === 0 && nodesToRenewZeroTimelock.length === 0) {
32485
32572
  return validNodes;
32486
32573
  }
32487
32574
  const nodesResp = await this.queryNodes({
@@ -32499,7 +32586,18 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
32499
32586
  nodesMap.set(node.id, node);
32500
32587
  }
32501
32588
  const nodesToAdd = [];
32502
- for (const node of nodesToRefresh) {
32589
+ for (const node of nodesToRenewNode) {
32590
+ if (!node.parentNodeId) {
32591
+ throw new Error(`node ${node.id} has no parent`);
32592
+ }
32593
+ const parentNode = nodesMap.get(node.parentNodeId);
32594
+ if (!parentNode) {
32595
+ throw new Error(`parent node ${node.parentNodeId} not found`);
32596
+ }
32597
+ const newNode = await this.transferService.renewNodeTxn(node, parentNode);
32598
+ nodesToAdd.push(newNode);
32599
+ }
32600
+ for (const node of nodesToRenewRefund) {
32503
32601
  if (!node.parentNodeId) {
32504
32602
  throw new Error(`node ${node.id} has no parent`);
32505
32603
  }
@@ -32507,17 +32605,14 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
32507
32605
  if (!parentNode) {
32508
32606
  throw new Error(`parent node ${node.parentNodeId} not found`);
32509
32607
  }
32510
- const { nodes: nodes2 } = await this.transferService.refreshTimelockNodes(
32608
+ const newNode = await this.transferService.renewRefundTxn(
32511
32609
  node,
32512
32610
  parentNode
32513
32611
  );
32514
- if (nodes2.length !== 1) {
32515
- throw new Error(`expected 1 node, got ${nodes2.length}`);
32516
- }
32517
- const newNode = nodes2[0];
32518
- if (!newNode) {
32519
- throw new Error("Failed to refresh timelock node");
32520
- }
32612
+ nodesToAdd.push(newNode);
32613
+ }
32614
+ for (const node of nodesToRenewZeroTimelock) {
32615
+ const newNode = await this.transferService.renewZeroTimelockNodeTxn(node);
32521
32616
  nodesToAdd.push(newNode);
32522
32617
  }
32523
32618
  this.updateLeaves(nodeIds, nodesToAdd);
@@ -32558,14 +32653,14 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
32558
32653
  return response.nodes;
32559
32654
  });
32560
32655
  }
32561
- async processClaimedTransferResults(result, transfer, emit, optimize) {
32562
- result = await this.checkRefreshTimelockNodes(result);
32563
- result = await this.checkExtendTimeLockNodes(result);
32656
+ async processClaimedTransferResults(result, transfer, emit) {
32657
+ result = await this.checkRenewLeaves(result);
32564
32658
  const existingIds = new Set(this.leaves.map((leaf) => leaf.id));
32565
32659
  const uniqueResults = result.filter((node) => !existingIds.has(node.id));
32566
32660
  this.leaves.push(...uniqueResults);
32567
- if (optimize && transfer.type !== 40 /* COUNTER_SWAP */) {
32568
- await this.optimizeLeaves();
32661
+ if (this.config.getOptimizationOptions().auto && transfer.type !== 40 /* COUNTER_SWAP */) {
32662
+ for await (const _ of this.optimizeLeaves()) {
32663
+ }
32569
32664
  }
32570
32665
  if (emit) {
32571
32666
  this.emit(
@@ -32584,8 +32679,7 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
32584
32679
  */
32585
32680
  async claimTransfer({
32586
32681
  transfer,
32587
- emit,
32588
- optimize
32682
+ emit
32589
32683
  }) {
32590
32684
  const onError = async (context) => {
32591
32685
  const error = context.error;
@@ -32630,12 +32724,7 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
32630
32724
  if (result.length === 0) {
32631
32725
  return [];
32632
32726
  }
32633
- return await this.processClaimedTransferResults(
32634
- result,
32635
- transfer,
32636
- emit,
32637
- optimize
32638
- );
32727
+ return await this.processClaimedTransferResults(result, transfer, emit);
32639
32728
  } catch (error) {
32640
32729
  console.warn(
32641
32730
  `Failed to claim transfer after all retries. Please try reinitializing your wallet in a few minutes. Transfer ID: ${transfer.id}`,
@@ -32668,7 +32757,7 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
32668
32757
  continue;
32669
32758
  }
32670
32759
  promises.push(
32671
- this.claimTransfer({ transfer, emit, optimize: true }).then(() => transfer.id).catch((error) => {
32760
+ this.claimTransfer({ transfer, emit }).then(() => transfer.id).catch((error) => {
32672
32761
  console.warn(`Failed to claim transfer ${transfer.id}:`, error);
32673
32762
  return null;
32674
32763
  })
@@ -32939,8 +33028,7 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
32939
33028
  selectedLeaves,
32940
33029
  `no leaves for ${totalAmount}`
32941
33030
  );
32942
- leaves = await this.checkRefreshTimelockNodes(leaves);
32943
- leaves = await this.checkExtendTimeLockNodes(leaves);
33031
+ leaves = await this.checkRenewLeaves(leaves);
32944
33032
  const leavesToSend = await Promise.all(
32945
33033
  leaves.map(async (leaf) => ({
32946
33034
  leaf,
@@ -33271,10 +33359,8 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
33271
33359
  leavesToSendToSsp = leavesForTargetAmount;
33272
33360
  leavesToSendToSE = leavesForFee;
33273
33361
  }
33274
- leavesToSendToSsp = await this.checkRefreshTimelockNodes(leavesToSendToSsp);
33275
- leavesToSendToSsp = await this.checkExtendTimeLockNodes(leavesToSendToSsp);
33276
- leavesToSendToSE = await this.checkRefreshTimelockNodes(leavesToSendToSE);
33277
- leavesToSendToSE = await this.checkExtendTimeLockNodes(leavesToSendToSE);
33362
+ leavesToSendToSsp = await this.checkRenewLeaves(leavesToSendToSsp);
33363
+ leavesToSendToSE = await this.checkRenewLeaves(leavesToSendToSE);
33278
33364
  const leafKeyTweaks = await Promise.all(
33279
33365
  [...leavesToSendToSE, ...leavesToSendToSsp].map(async (leaf) => ({
33280
33366
  leaf,
@@ -33360,8 +33446,7 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
33360
33446
  (await this.selectLeaves([amountSats])).get(amountSats),
33361
33447
  `no leaves for ${amountSats}`
33362
33448
  );
33363
- leaves = await this.checkRefreshTimelockNodes(leaves);
33364
- leaves = await this.checkExtendTimeLockNodes(leaves);
33449
+ leaves = await this.checkRenewLeaves(leaves);
33365
33450
  const feeEstimate = await sspClient.getCoopExitFeeQuote({
33366
33451
  leafExternalIds: leaves.map((leaf) => leaf.id),
33367
33452
  withdrawalAddress
@@ -33649,7 +33734,7 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
33649
33734
  */
33650
33735
  async signTransaction(txHex, keyType = "auto-detect") {
33651
33736
  try {
33652
- const tx = import_btc_signer7.Transaction.fromRaw((0, import_utils24.hexToBytes)(txHex));
33737
+ const tx = import_btc_signer5.Transaction.fromRaw((0, import_utils24.hexToBytes)(txHex));
33653
33738
  let publicKey;
33654
33739
  switch (keyType.toLowerCase()) {
33655
33740
  case "identity":
@@ -33856,15 +33941,6 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
33856
33941
  }
33857
33942
  );
33858
33943
  }
33859
- if (!nodeInput.sequence) {
33860
- throw new ValidationError(
33861
- `Node transaction has no sequence for ${isRootNode ? "root" : "non-root"} node`,
33862
- {
33863
- field: "sequence",
33864
- value: nodeInput.sequence
33865
- }
33866
- );
33867
- }
33868
33944
  const refundInput = refundTx.getInput(0);
33869
33945
  if (!refundInput) {
33870
33946
  throw new ValidationError(
@@ -33900,89 +33976,6 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
33900
33976
  );
33901
33977
  }
33902
33978
  }
33903
- /**
33904
- * Refresh the timelock of a specific node.
33905
- *
33906
- * @param {string} nodeId - The ID of the node to refresh
33907
- * @returns {Promise<void>} Promise that resolves when the timelock is refreshed
33908
- */
33909
- async testOnly_expireTimelock(nodeId) {
33910
- const sparkClient = await this.connectionManager.createSparkClient(
33911
- this.config.getCoordinatorAddress()
33912
- );
33913
- try {
33914
- const response = await sparkClient.query_nodes({
33915
- source: {
33916
- $case: "nodeIds",
33917
- nodeIds: {
33918
- nodeIds: [nodeId]
33919
- }
33920
- },
33921
- includeParents: true
33922
- });
33923
- let leaf = response.nodes[nodeId];
33924
- if (!leaf) {
33925
- throw new ValidationError("Node not found", {
33926
- field: "nodeId",
33927
- value: nodeId
33928
- });
33929
- }
33930
- let parentNode;
33931
- let hasParentNode = false;
33932
- if (!leaf.parentNodeId) {
33933
- } else {
33934
- hasParentNode = true;
33935
- parentNode = response.nodes[leaf.parentNodeId];
33936
- if (!parentNode) {
33937
- throw new ValidationError("Parent node not found", {
33938
- field: "parentNodeId",
33939
- value: leaf.parentNodeId
33940
- });
33941
- }
33942
- }
33943
- const nodeTx = getTxFromRawTxBytes(leaf.nodeTx);
33944
- const refundTx = getTxFromRawTxBytes(leaf.refundTx);
33945
- if (hasParentNode) {
33946
- const nodeTimelock = getCurrentTimelock(nodeTx.getInput(0).sequence);
33947
- if (nodeTimelock > 100) {
33948
- const expiredNodeTxLeaf = await this.transferService.testonly_expireTimeLockNodeTx(
33949
- leaf,
33950
- parentNode
33951
- );
33952
- if (!expiredNodeTxLeaf.nodes[0]) {
33953
- throw new ValidationError("No expired node tx leaf", {
33954
- field: "expiredNodeTxLeaf",
33955
- value: expiredNodeTxLeaf
33956
- });
33957
- }
33958
- leaf = expiredNodeTxLeaf.nodes[0];
33959
- }
33960
- }
33961
- const refundTimelock = getCurrentTimelock(refundTx.getInput(0).sequence);
33962
- if (refundTimelock > 100) {
33963
- const expiredTxLeaf = await this.transferService.testonly_expireTimeLockRefundtx(leaf);
33964
- if (!expiredTxLeaf.nodes[0]) {
33965
- throw new ValidationError("No expired tx leaf", {
33966
- field: "expiredTxLeaf",
33967
- value: expiredTxLeaf
33968
- });
33969
- }
33970
- leaf = expiredTxLeaf.nodes[0];
33971
- }
33972
- const leafIndex = this.leaves.findIndex((leaf2) => leaf2.id === leaf2.id);
33973
- if (leafIndex !== -1) {
33974
- this.leaves[leafIndex] = leaf;
33975
- }
33976
- } catch (error) {
33977
- throw new NetworkError(
33978
- "Failed to refresh timelock",
33979
- {
33980
- method: "refresh_timelock"
33981
- },
33982
- error
33983
- );
33984
- }
33985
- }
33986
33979
  cleanup() {
33987
33980
  if (this.claimTransfersInterval) {
33988
33981
  clearInterval(this.claimTransfersInterval);
@@ -34137,8 +34130,7 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
34137
34130
  "getLightningReceiveRequest",
34138
34131
  "getLightningSendRequest",
34139
34132
  "getCoopExitRequest",
34140
- "checkTimelock",
34141
- "testOnly_expireTimelock"
34133
+ "checkTimelock"
34142
34134
  ];
34143
34135
  methods.forEach((m) => this.wrapPublicSparkWalletMethodWithOtelSpan(m));
34144
34136
  this.initWallet = this.wrapWithOtelSpan(
@@ -35718,7 +35710,6 @@ setFetch(sparkBareFetch, Headers2);
35718
35710
  DIRECT_TIMELOCK_OFFSET,
35719
35711
  DefaultSparkSigner,
35720
35712
  HTLC_TIMELOCK_OFFSET,
35721
- INITIAL_DIRECT_SEQUENCE,
35722
35713
  INITIAL_SEQUENCE,
35723
35714
  InternalValidationError,
35724
35715
  LOGGER_NAMES,
@@ -35751,21 +35742,24 @@ setFetch(sparkBareFetch, Headers2);
35751
35742
  constructFeeBumpTx,
35752
35743
  constructUnilateralExitFeeBumpPackages,
35753
35744
  constructUnilateralExitTxs,
35754
- createConnectorRefundTransactions,
35755
- createLeafNodeTx,
35756
- createNodeTx,
35757
- createNodeTxs,
35758
- createRefundTx,
35759
- createRefundTxs,
35760
- createRootTx,
35745
+ createConnectorRefundTxs,
35746
+ createCurrentTimelockRefundTxs,
35747
+ createDecrementedTimelockNodeTx,
35748
+ createDecrementedTimelockRefundTxs,
35749
+ createInitialTimelockNodeTx,
35750
+ createInitialTimelockRefundTxs,
35751
+ createRootNodeTx,
35761
35752
  createSigningCommitment,
35762
35753
  createSigningNonce,
35763
- createSplitTx,
35754
+ createTestUnilateralRefundTxs,
35755
+ createTestUnilateralTimelockNodeTx,
35756
+ createZeroTimelockNodeTx,
35764
35757
  decodeBech32mTokenIdentifier,
35765
35758
  decodeBytesToSigningCommitment,
35766
35759
  decodeBytesToSigningNonce,
35767
35760
  decodeSparkAddress,
35768
35761
  doesLeafNeedRefresh,
35762
+ doesTxnNeedRenewed,
35769
35763
  encodeBech32mTokenIdentifier,
35770
35764
  encodeSigningCommitmentToBytes,
35771
35765
  encodeSigningNonceToBytes,
@@ -35803,12 +35797,14 @@ setFetch(sparkBareFetch, Headers2);
35803
35797
  getTxFromRawTxHex,
35804
35798
  getTxId,
35805
35799
  getTxIdNoReverse,
35800
+ hash160,
35806
35801
  isEphemeralAnchorOutput,
35807
35802
  isLegacySparkAddress,
35808
35803
  isSafeForNumber,
35809
35804
  isTxBroadcast,
35810
35805
  isValidPublicKey,
35811
35806
  isValidSparkAddress,
35807
+ isZeroTimelock,
35812
35808
  lastKeyWithTarget,
35813
35809
  maybeApplyFee,
35814
35810
  modInverse,