@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
package/dist/index.cjs CHANGED
@@ -1111,30 +1111,30 @@ function signFrost({
1111
1111
  adaptorPubKey
1112
1112
  }) {
1113
1113
  const logMsg = JSON.stringify({
1114
- message: (0, import_utils12.bytesToHex)(message),
1114
+ message: (0, import_utils13.bytesToHex)(message),
1115
1115
  keyPackage: {
1116
- secretKey: (0, import_utils12.bytesToHex)(keyPackage.secretKey),
1117
- publicKey: (0, import_utils12.bytesToHex)(keyPackage.publicKey),
1118
- verifyingKey: (0, import_utils12.bytesToHex)(keyPackage.verifyingKey)
1116
+ secretKey: (0, import_utils13.bytesToHex)(keyPackage.secretKey),
1117
+ publicKey: (0, import_utils13.bytesToHex)(keyPackage.publicKey),
1118
+ verifyingKey: (0, import_utils13.bytesToHex)(keyPackage.verifyingKey)
1119
1119
  },
1120
1120
  nonce: {
1121
- hiding: (0, import_utils12.bytesToHex)(nonce.hiding),
1122
- binding: (0, import_utils12.bytesToHex)(nonce.binding)
1121
+ hiding: (0, import_utils13.bytesToHex)(nonce.hiding),
1122
+ binding: (0, import_utils13.bytesToHex)(nonce.binding)
1123
1123
  },
1124
1124
  selfCommitment: {
1125
- hiding: (0, import_utils12.bytesToHex)(selfCommitment.hiding),
1126
- binding: (0, import_utils12.bytesToHex)(selfCommitment.binding)
1125
+ hiding: (0, import_utils13.bytesToHex)(selfCommitment.hiding),
1126
+ binding: (0, import_utils13.bytesToHex)(selfCommitment.binding)
1127
1127
  },
1128
1128
  statechainCommitments: Object.fromEntries(
1129
1129
  Object.entries(statechainCommitments ?? {}).map(([k, v]) => [
1130
1130
  k,
1131
1131
  {
1132
- hiding: (0, import_utils12.bytesToHex)(v.hiding),
1133
- binding: (0, import_utils12.bytesToHex)(v.binding)
1132
+ hiding: (0, import_utils13.bytesToHex)(v.hiding),
1133
+ binding: (0, import_utils13.bytesToHex)(v.binding)
1134
1134
  }
1135
1135
  ])
1136
1136
  ),
1137
- adaptorPubKey: adaptorPubKey ? (0, import_utils12.bytesToHex)(adaptorPubKey) : void 0
1137
+ adaptorPubKey: adaptorPubKey ? (0, import_utils13.bytesToHex)(adaptorPubKey) : void 0
1138
1138
  });
1139
1139
  SparkSdkLogger.get(LOGGER_NAMES.wasm).trace("signFrost", logMsg);
1140
1140
  const result = wasm_sign_frost(
@@ -1147,7 +1147,7 @@ function signFrost({
1147
1147
  );
1148
1148
  SparkSdkLogger.get(LOGGER_NAMES.wasm).trace(
1149
1149
  "signFrost result",
1150
- (0, import_utils12.bytesToHex)(result)
1150
+ (0, import_utils13.bytesToHex)(result)
1151
1151
  );
1152
1152
  return result;
1153
1153
  }
@@ -1163,36 +1163,36 @@ function aggregateFrost({
1163
1163
  adaptorPubKey
1164
1164
  }) {
1165
1165
  const logMsg = JSON.stringify({
1166
- message: (0, import_utils12.bytesToHex)(message),
1166
+ message: (0, import_utils13.bytesToHex)(message),
1167
1167
  statechainCommitments: Object.fromEntries(
1168
1168
  Object.entries(statechainCommitments ?? {}).map(([k, v]) => [
1169
1169
  k,
1170
1170
  {
1171
- hiding: (0, import_utils12.bytesToHex)(v.hiding),
1172
- binding: (0, import_utils12.bytesToHex)(v.binding)
1171
+ hiding: (0, import_utils13.bytesToHex)(v.hiding),
1172
+ binding: (0, import_utils13.bytesToHex)(v.binding)
1173
1173
  }
1174
1174
  ])
1175
1175
  ),
1176
1176
  selfCommitment: {
1177
- hiding: (0, import_utils12.bytesToHex)(selfCommitment.hiding),
1178
- binding: (0, import_utils12.bytesToHex)(selfCommitment.binding)
1177
+ hiding: (0, import_utils13.bytesToHex)(selfCommitment.hiding),
1178
+ binding: (0, import_utils13.bytesToHex)(selfCommitment.binding)
1179
1179
  },
1180
1180
  statechainSignatures: Object.fromEntries(
1181
1181
  Object.entries(statechainSignatures ?? {}).map(([k, v]) => [
1182
1182
  k,
1183
- (0, import_utils12.bytesToHex)(v)
1183
+ (0, import_utils13.bytesToHex)(v)
1184
1184
  ])
1185
1185
  ),
1186
- selfSignature: (0, import_utils12.bytesToHex)(selfSignature),
1186
+ selfSignature: (0, import_utils13.bytesToHex)(selfSignature),
1187
1187
  statechainPublicKeys: Object.fromEntries(
1188
1188
  Object.entries(statechainPublicKeys ?? {}).map(([k, v]) => [
1189
1189
  k,
1190
- (0, import_utils12.bytesToHex)(v)
1190
+ (0, import_utils13.bytesToHex)(v)
1191
1191
  ])
1192
1192
  ),
1193
- selfPublicKey: (0, import_utils12.bytesToHex)(selfPublicKey),
1194
- verifyingKey: (0, import_utils12.bytesToHex)(verifyingKey),
1195
- adaptorPubKey: adaptorPubKey ? (0, import_utils12.bytesToHex)(adaptorPubKey) : void 0
1193
+ selfPublicKey: (0, import_utils13.bytesToHex)(selfPublicKey),
1194
+ verifyingKey: (0, import_utils13.bytesToHex)(verifyingKey),
1195
+ adaptorPubKey: adaptorPubKey ? (0, import_utils13.bytesToHex)(adaptorPubKey) : void 0
1196
1196
  });
1197
1197
  SparkSdkLogger.get(LOGGER_NAMES.wasm).trace("aggregateFrost", logMsg);
1198
1198
  const result = wasm_aggregate_frost(
@@ -1208,7 +1208,7 @@ function aggregateFrost({
1208
1208
  );
1209
1209
  SparkSdkLogger.get(LOGGER_NAMES.wasm).trace(
1210
1210
  "aggregateFrost result",
1211
- (0, import_utils12.bytesToHex)(result)
1211
+ (0, import_utils13.bytesToHex)(result)
1212
1212
  );
1213
1213
  return result;
1214
1214
  }
@@ -1230,12 +1230,12 @@ function decryptEcies({
1230
1230
  }) {
1231
1231
  return decrypt_ecies(encryptedMsg, privateKey);
1232
1232
  }
1233
- var import_utils12;
1233
+ var import_utils13;
1234
1234
  var init_wasm = __esm({
1235
1235
  "src/spark_bindings/wasm/index.ts"() {
1236
1236
  "use strict";
1237
1237
  init_buffer();
1238
- import_utils12 = require("@noble/curves/utils");
1238
+ import_utils13 = require("@noble/curves/utils");
1239
1239
  init_spark_bindings();
1240
1240
  init_logging();
1241
1241
  }
@@ -1252,7 +1252,6 @@ __export(index_exports, {
1252
1252
  DIRECT_TIMELOCK_OFFSET: () => DIRECT_TIMELOCK_OFFSET,
1253
1253
  DefaultSparkSigner: () => DefaultSparkSigner,
1254
1254
  HTLC_TIMELOCK_OFFSET: () => HTLC_TIMELOCK_OFFSET,
1255
- INITIAL_DIRECT_SEQUENCE: () => INITIAL_DIRECT_SEQUENCE,
1256
1255
  INITIAL_SEQUENCE: () => INITIAL_SEQUENCE,
1257
1256
  InternalValidationError: () => InternalValidationError,
1258
1257
  KeyDerivationType: () => KeyDerivationType,
@@ -1289,21 +1288,24 @@ __export(index_exports, {
1289
1288
  constructFeeBumpTx: () => constructFeeBumpTx,
1290
1289
  constructUnilateralExitFeeBumpPackages: () => constructUnilateralExitFeeBumpPackages,
1291
1290
  constructUnilateralExitTxs: () => constructUnilateralExitTxs,
1292
- createConnectorRefundTransactions: () => createConnectorRefundTransactions,
1293
- createLeafNodeTx: () => createLeafNodeTx,
1294
- createNodeTx: () => createNodeTx,
1295
- createNodeTxs: () => createNodeTxs,
1296
- createRefundTx: () => createRefundTx,
1297
- createRefundTxs: () => createRefundTxs,
1298
- createRootTx: () => createRootTx,
1291
+ createConnectorRefundTxs: () => createConnectorRefundTxs,
1292
+ createCurrentTimelockRefundTxs: () => createCurrentTimelockRefundTxs,
1293
+ createDecrementedTimelockNodeTx: () => createDecrementedTimelockNodeTx,
1294
+ createDecrementedTimelockRefundTxs: () => createDecrementedTimelockRefundTxs,
1295
+ createInitialTimelockNodeTx: () => createInitialTimelockNodeTx,
1296
+ createInitialTimelockRefundTxs: () => createInitialTimelockRefundTxs,
1297
+ createRootNodeTx: () => createRootNodeTx,
1299
1298
  createSigningCommitment: () => createSigningCommitment,
1300
1299
  createSigningNonce: () => createSigningNonce,
1301
- createSplitTx: () => createSplitTx,
1300
+ createTestUnilateralRefundTxs: () => createTestUnilateralRefundTxs,
1301
+ createTestUnilateralTimelockNodeTx: () => createTestUnilateralTimelockNodeTx,
1302
+ createZeroTimelockNodeTx: () => createZeroTimelockNodeTx,
1302
1303
  decodeBech32mTokenIdentifier: () => decodeBech32mTokenIdentifier,
1303
1304
  decodeBytesToSigningCommitment: () => decodeBytesToSigningCommitment,
1304
1305
  decodeBytesToSigningNonce: () => decodeBytesToSigningNonce,
1305
1306
  decodeSparkAddress: () => decodeSparkAddress,
1306
1307
  doesLeafNeedRefresh: () => doesLeafNeedRefresh,
1308
+ doesTxnNeedRenewed: () => doesTxnNeedRenewed,
1307
1309
  encodeBech32mTokenIdentifier: () => encodeBech32mTokenIdentifier,
1308
1310
  encodeSigningCommitmentToBytes: () => encodeSigningCommitmentToBytes,
1309
1311
  encodeSigningNonceToBytes: () => encodeSigningNonceToBytes,
@@ -1341,6 +1343,7 @@ __export(index_exports, {
1341
1343
  getTxFromRawTxHex: () => getTxFromRawTxHex,
1342
1344
  getTxId: () => getTxId,
1343
1345
  getTxIdNoReverse: () => getTxIdNoReverse,
1346
+ hash160: () => hash160,
1344
1347
  initializeTracerEnv: () => initializeTracerEnvBrowser,
1345
1348
  isEphemeralAnchorOutput: () => isEphemeralAnchorOutput,
1346
1349
  isLegacySparkAddress: () => isLegacySparkAddress,
@@ -1348,6 +1351,7 @@ __export(index_exports, {
1348
1351
  isTxBroadcast: () => isTxBroadcast,
1349
1352
  isValidPublicKey: () => isValidPublicKey,
1350
1353
  isValidSparkAddress: () => isValidSparkAddress,
1354
+ isZeroTimelock: () => isZeroTimelock,
1351
1355
  lastKeyWithTarget: () => lastKeyWithTarget,
1352
1356
  maybeApplyFee: () => maybeApplyFee,
1353
1357
  modInverse: () => modInverse,
@@ -3947,6 +3951,12 @@ var RenewLeafRequest = {
3947
3951
  writer.uint32(26).fork()
3948
3952
  ).join();
3949
3953
  break;
3954
+ case "renewNodeZeroTimelockSigningJob":
3955
+ RenewNodeZeroTimelockSigningJob.encode(
3956
+ message.signingJobs.renewNodeZeroTimelockSigningJob,
3957
+ writer.uint32(34).fork()
3958
+ ).join();
3959
+ break;
3950
3960
  }
3951
3961
  return writer;
3952
3962
  },
@@ -3984,6 +3994,16 @@ var RenewLeafRequest = {
3984
3994
  };
3985
3995
  continue;
3986
3996
  }
3997
+ case 4: {
3998
+ if (tag !== 34) {
3999
+ break;
4000
+ }
4001
+ message.signingJobs = {
4002
+ $case: "renewNodeZeroTimelockSigningJob",
4003
+ renewNodeZeroTimelockSigningJob: RenewNodeZeroTimelockSigningJob.decode(reader, reader.uint32())
4004
+ };
4005
+ continue;
4006
+ }
3987
4007
  }
3988
4008
  if ((tag & 7) === 4 || tag === 0) {
3989
4009
  break;
@@ -4001,6 +4021,11 @@ var RenewLeafRequest = {
4001
4021
  } : isSet3(object.renewRefundTimelockSigningJob) ? {
4002
4022
  $case: "renewRefundTimelockSigningJob",
4003
4023
  renewRefundTimelockSigningJob: RenewRefundTimelockSigningJob.fromJSON(object.renewRefundTimelockSigningJob)
4024
+ } : isSet3(object.renewNodeZeroTimelockSigningJob) ? {
4025
+ $case: "renewNodeZeroTimelockSigningJob",
4026
+ renewNodeZeroTimelockSigningJob: RenewNodeZeroTimelockSigningJob.fromJSON(
4027
+ object.renewNodeZeroTimelockSigningJob
4028
+ )
4004
4029
  } : void 0
4005
4030
  };
4006
4031
  },
@@ -4017,6 +4042,10 @@ var RenewLeafRequest = {
4017
4042
  obj.renewRefundTimelockSigningJob = RenewRefundTimelockSigningJob.toJSON(
4018
4043
  message.signingJobs.renewRefundTimelockSigningJob
4019
4044
  );
4045
+ } else if (message.signingJobs?.$case === "renewNodeZeroTimelockSigningJob") {
4046
+ obj.renewNodeZeroTimelockSigningJob = RenewNodeZeroTimelockSigningJob.toJSON(
4047
+ message.signingJobs.renewNodeZeroTimelockSigningJob
4048
+ );
4020
4049
  }
4021
4050
  return obj;
4022
4051
  },
@@ -4049,6 +4078,17 @@ var RenewLeafRequest = {
4049
4078
  }
4050
4079
  break;
4051
4080
  }
4081
+ case "renewNodeZeroTimelockSigningJob": {
4082
+ if (object.signingJobs?.renewNodeZeroTimelockSigningJob !== void 0 && object.signingJobs?.renewNodeZeroTimelockSigningJob !== null) {
4083
+ message.signingJobs = {
4084
+ $case: "renewNodeZeroTimelockSigningJob",
4085
+ renewNodeZeroTimelockSigningJob: RenewNodeZeroTimelockSigningJob.fromPartial(
4086
+ object.signingJobs.renewNodeZeroTimelockSigningJob
4087
+ )
4088
+ };
4089
+ }
4090
+ break;
4091
+ }
4052
4092
  }
4053
4093
  return message;
4054
4094
  }
@@ -4323,6 +4363,125 @@ var RenewRefundTimelockSigningJob = {
4323
4363
  return message;
4324
4364
  }
4325
4365
  };
4366
+ function createBaseRenewNodeZeroTimelockSigningJob() {
4367
+ return {
4368
+ nodeTxSigningJob: void 0,
4369
+ refundTxSigningJob: void 0,
4370
+ directNodeTxSigningJob: void 0,
4371
+ directRefundTxSigningJob: void 0,
4372
+ directFromCpfpRefundTxSigningJob: void 0
4373
+ };
4374
+ }
4375
+ var RenewNodeZeroTimelockSigningJob = {
4376
+ encode(message, writer = new import_wire4.BinaryWriter()) {
4377
+ if (message.nodeTxSigningJob !== void 0) {
4378
+ UserSignedTxSigningJob.encode(message.nodeTxSigningJob, writer.uint32(10).fork()).join();
4379
+ }
4380
+ if (message.refundTxSigningJob !== void 0) {
4381
+ UserSignedTxSigningJob.encode(message.refundTxSigningJob, writer.uint32(18).fork()).join();
4382
+ }
4383
+ if (message.directNodeTxSigningJob !== void 0) {
4384
+ UserSignedTxSigningJob.encode(message.directNodeTxSigningJob, writer.uint32(26).fork()).join();
4385
+ }
4386
+ if (message.directRefundTxSigningJob !== void 0) {
4387
+ UserSignedTxSigningJob.encode(message.directRefundTxSigningJob, writer.uint32(34).fork()).join();
4388
+ }
4389
+ if (message.directFromCpfpRefundTxSigningJob !== void 0) {
4390
+ UserSignedTxSigningJob.encode(message.directFromCpfpRefundTxSigningJob, writer.uint32(42).fork()).join();
4391
+ }
4392
+ return writer;
4393
+ },
4394
+ decode(input, length) {
4395
+ const reader = input instanceof import_wire4.BinaryReader ? input : new import_wire4.BinaryReader(input);
4396
+ const end = length === void 0 ? reader.len : reader.pos + length;
4397
+ const message = createBaseRenewNodeZeroTimelockSigningJob();
4398
+ while (reader.pos < end) {
4399
+ const tag = reader.uint32();
4400
+ switch (tag >>> 3) {
4401
+ case 1: {
4402
+ if (tag !== 10) {
4403
+ break;
4404
+ }
4405
+ message.nodeTxSigningJob = UserSignedTxSigningJob.decode(reader, reader.uint32());
4406
+ continue;
4407
+ }
4408
+ case 2: {
4409
+ if (tag !== 18) {
4410
+ break;
4411
+ }
4412
+ message.refundTxSigningJob = UserSignedTxSigningJob.decode(reader, reader.uint32());
4413
+ continue;
4414
+ }
4415
+ case 3: {
4416
+ if (tag !== 26) {
4417
+ break;
4418
+ }
4419
+ message.directNodeTxSigningJob = UserSignedTxSigningJob.decode(reader, reader.uint32());
4420
+ continue;
4421
+ }
4422
+ case 4: {
4423
+ if (tag !== 34) {
4424
+ break;
4425
+ }
4426
+ message.directRefundTxSigningJob = UserSignedTxSigningJob.decode(reader, reader.uint32());
4427
+ continue;
4428
+ }
4429
+ case 5: {
4430
+ if (tag !== 42) {
4431
+ break;
4432
+ }
4433
+ message.directFromCpfpRefundTxSigningJob = UserSignedTxSigningJob.decode(reader, reader.uint32());
4434
+ continue;
4435
+ }
4436
+ }
4437
+ if ((tag & 7) === 4 || tag === 0) {
4438
+ break;
4439
+ }
4440
+ reader.skip(tag & 7);
4441
+ }
4442
+ return message;
4443
+ },
4444
+ fromJSON(object) {
4445
+ return {
4446
+ nodeTxSigningJob: isSet3(object.nodeTxSigningJob) ? UserSignedTxSigningJob.fromJSON(object.nodeTxSigningJob) : void 0,
4447
+ refundTxSigningJob: isSet3(object.refundTxSigningJob) ? UserSignedTxSigningJob.fromJSON(object.refundTxSigningJob) : void 0,
4448
+ directNodeTxSigningJob: isSet3(object.directNodeTxSigningJob) ? UserSignedTxSigningJob.fromJSON(object.directNodeTxSigningJob) : void 0,
4449
+ directRefundTxSigningJob: isSet3(object.directRefundTxSigningJob) ? UserSignedTxSigningJob.fromJSON(object.directRefundTxSigningJob) : void 0,
4450
+ directFromCpfpRefundTxSigningJob: isSet3(object.directFromCpfpRefundTxSigningJob) ? UserSignedTxSigningJob.fromJSON(object.directFromCpfpRefundTxSigningJob) : void 0
4451
+ };
4452
+ },
4453
+ toJSON(message) {
4454
+ const obj = {};
4455
+ if (message.nodeTxSigningJob !== void 0) {
4456
+ obj.nodeTxSigningJob = UserSignedTxSigningJob.toJSON(message.nodeTxSigningJob);
4457
+ }
4458
+ if (message.refundTxSigningJob !== void 0) {
4459
+ obj.refundTxSigningJob = UserSignedTxSigningJob.toJSON(message.refundTxSigningJob);
4460
+ }
4461
+ if (message.directNodeTxSigningJob !== void 0) {
4462
+ obj.directNodeTxSigningJob = UserSignedTxSigningJob.toJSON(message.directNodeTxSigningJob);
4463
+ }
4464
+ if (message.directRefundTxSigningJob !== void 0) {
4465
+ obj.directRefundTxSigningJob = UserSignedTxSigningJob.toJSON(message.directRefundTxSigningJob);
4466
+ }
4467
+ if (message.directFromCpfpRefundTxSigningJob !== void 0) {
4468
+ obj.directFromCpfpRefundTxSigningJob = UserSignedTxSigningJob.toJSON(message.directFromCpfpRefundTxSigningJob);
4469
+ }
4470
+ return obj;
4471
+ },
4472
+ create(base) {
4473
+ return RenewNodeZeroTimelockSigningJob.fromPartial(base ?? {});
4474
+ },
4475
+ fromPartial(object) {
4476
+ const message = createBaseRenewNodeZeroTimelockSigningJob();
4477
+ message.nodeTxSigningJob = object.nodeTxSigningJob !== void 0 && object.nodeTxSigningJob !== null ? UserSignedTxSigningJob.fromPartial(object.nodeTxSigningJob) : void 0;
4478
+ message.refundTxSigningJob = object.refundTxSigningJob !== void 0 && object.refundTxSigningJob !== null ? UserSignedTxSigningJob.fromPartial(object.refundTxSigningJob) : void 0;
4479
+ message.directNodeTxSigningJob = object.directNodeTxSigningJob !== void 0 && object.directNodeTxSigningJob !== null ? UserSignedTxSigningJob.fromPartial(object.directNodeTxSigningJob) : void 0;
4480
+ message.directRefundTxSigningJob = object.directRefundTxSigningJob !== void 0 && object.directRefundTxSigningJob !== null ? UserSignedTxSigningJob.fromPartial(object.directRefundTxSigningJob) : void 0;
4481
+ message.directFromCpfpRefundTxSigningJob = object.directFromCpfpRefundTxSigningJob !== void 0 && object.directFromCpfpRefundTxSigningJob !== null ? UserSignedTxSigningJob.fromPartial(object.directFromCpfpRefundTxSigningJob) : void 0;
4482
+ return message;
4483
+ }
4484
+ };
4326
4485
  function createBaseRenewLeafResponse() {
4327
4486
  return { renewResult: void 0 };
4328
4487
  }
@@ -4335,6 +4494,9 @@ var RenewLeafResponse = {
4335
4494
  case "renewRefundTimelockResult":
4336
4495
  RenewRefundTimelockResult.encode(message.renewResult.renewRefundTimelockResult, writer.uint32(18).fork()).join();
4337
4496
  break;
4497
+ case "renewNodeZeroTimelockResult":
4498
+ RenewNodeZeroTimelockResult.encode(message.renewResult.renewNodeZeroTimelockResult, writer.uint32(26).fork()).join();
4499
+ break;
4338
4500
  }
4339
4501
  return writer;
4340
4502
  },
@@ -4365,6 +4527,16 @@ var RenewLeafResponse = {
4365
4527
  };
4366
4528
  continue;
4367
4529
  }
4530
+ case 3: {
4531
+ if (tag !== 26) {
4532
+ break;
4533
+ }
4534
+ message.renewResult = {
4535
+ $case: "renewNodeZeroTimelockResult",
4536
+ renewNodeZeroTimelockResult: RenewNodeZeroTimelockResult.decode(reader, reader.uint32())
4537
+ };
4538
+ continue;
4539
+ }
4368
4540
  }
4369
4541
  if ((tag & 7) === 4 || tag === 0) {
4370
4542
  break;
@@ -4381,6 +4553,9 @@ var RenewLeafResponse = {
4381
4553
  } : isSet3(object.renewRefundTimelockResult) ? {
4382
4554
  $case: "renewRefundTimelockResult",
4383
4555
  renewRefundTimelockResult: RenewRefundTimelockResult.fromJSON(object.renewRefundTimelockResult)
4556
+ } : isSet3(object.renewNodeZeroTimelockResult) ? {
4557
+ $case: "renewNodeZeroTimelockResult",
4558
+ renewNodeZeroTimelockResult: RenewNodeZeroTimelockResult.fromJSON(object.renewNodeZeroTimelockResult)
4384
4559
  } : void 0
4385
4560
  };
4386
4561
  },
@@ -4390,6 +4565,10 @@ var RenewLeafResponse = {
4390
4565
  obj.renewNodeTimelockResult = RenewNodeTimelockResult.toJSON(message.renewResult.renewNodeTimelockResult);
4391
4566
  } else if (message.renewResult?.$case === "renewRefundTimelockResult") {
4392
4567
  obj.renewRefundTimelockResult = RenewRefundTimelockResult.toJSON(message.renewResult.renewRefundTimelockResult);
4568
+ } else if (message.renewResult?.$case === "renewNodeZeroTimelockResult") {
4569
+ obj.renewNodeZeroTimelockResult = RenewNodeZeroTimelockResult.toJSON(
4570
+ message.renewResult.renewNodeZeroTimelockResult
4571
+ );
4393
4572
  }
4394
4573
  return obj;
4395
4574
  },
@@ -4419,6 +4598,17 @@ var RenewLeafResponse = {
4419
4598
  }
4420
4599
  break;
4421
4600
  }
4601
+ case "renewNodeZeroTimelockResult": {
4602
+ if (object.renewResult?.renewNodeZeroTimelockResult !== void 0 && object.renewResult?.renewNodeZeroTimelockResult !== null) {
4603
+ message.renewResult = {
4604
+ $case: "renewNodeZeroTimelockResult",
4605
+ renewNodeZeroTimelockResult: RenewNodeZeroTimelockResult.fromPartial(
4606
+ object.renewResult.renewNodeZeroTimelockResult
4607
+ )
4608
+ };
4609
+ }
4610
+ break;
4611
+ }
4422
4612
  }
4423
4613
  return message;
4424
4614
  }
@@ -4542,6 +4732,74 @@ var RenewRefundTimelockResult = {
4542
4732
  return message;
4543
4733
  }
4544
4734
  };
4735
+ function createBaseRenewNodeZeroTimelockResult() {
4736
+ return { splitNode: void 0, node: void 0 };
4737
+ }
4738
+ var RenewNodeZeroTimelockResult = {
4739
+ encode(message, writer = new import_wire4.BinaryWriter()) {
4740
+ if (message.splitNode !== void 0) {
4741
+ TreeNode.encode(message.splitNode, writer.uint32(10).fork()).join();
4742
+ }
4743
+ if (message.node !== void 0) {
4744
+ TreeNode.encode(message.node, writer.uint32(18).fork()).join();
4745
+ }
4746
+ return writer;
4747
+ },
4748
+ decode(input, length) {
4749
+ const reader = input instanceof import_wire4.BinaryReader ? input : new import_wire4.BinaryReader(input);
4750
+ const end = length === void 0 ? reader.len : reader.pos + length;
4751
+ const message = createBaseRenewNodeZeroTimelockResult();
4752
+ while (reader.pos < end) {
4753
+ const tag = reader.uint32();
4754
+ switch (tag >>> 3) {
4755
+ case 1: {
4756
+ if (tag !== 10) {
4757
+ break;
4758
+ }
4759
+ message.splitNode = TreeNode.decode(reader, reader.uint32());
4760
+ continue;
4761
+ }
4762
+ case 2: {
4763
+ if (tag !== 18) {
4764
+ break;
4765
+ }
4766
+ message.node = TreeNode.decode(reader, reader.uint32());
4767
+ continue;
4768
+ }
4769
+ }
4770
+ if ((tag & 7) === 4 || tag === 0) {
4771
+ break;
4772
+ }
4773
+ reader.skip(tag & 7);
4774
+ }
4775
+ return message;
4776
+ },
4777
+ fromJSON(object) {
4778
+ return {
4779
+ splitNode: isSet3(object.splitNode) ? TreeNode.fromJSON(object.splitNode) : void 0,
4780
+ node: isSet3(object.node) ? TreeNode.fromJSON(object.node) : void 0
4781
+ };
4782
+ },
4783
+ toJSON(message) {
4784
+ const obj = {};
4785
+ if (message.splitNode !== void 0) {
4786
+ obj.splitNode = TreeNode.toJSON(message.splitNode);
4787
+ }
4788
+ if (message.node !== void 0) {
4789
+ obj.node = TreeNode.toJSON(message.node);
4790
+ }
4791
+ return obj;
4792
+ },
4793
+ create(base) {
4794
+ return RenewNodeZeroTimelockResult.fromPartial(base ?? {});
4795
+ },
4796
+ fromPartial(object) {
4797
+ const message = createBaseRenewNodeZeroTimelockResult();
4798
+ message.splitNode = object.splitNode !== void 0 && object.splitNode !== null ? TreeNode.fromPartial(object.splitNode) : void 0;
4799
+ message.node = object.node !== void 0 && object.node !== null ? TreeNode.fromPartial(object.node) : void 0;
4800
+ return message;
4801
+ }
4802
+ };
4545
4803
  function createBaseNodeSignatureShares() {
4546
4804
  return {
4547
4805
  nodeId: "",
@@ -18244,7 +18502,11 @@ var BASE_CONFIG = {
18244
18502
  console: {
18245
18503
  otel: false
18246
18504
  },
18247
- events: {}
18505
+ events: {},
18506
+ optimizationOptions: {
18507
+ auto: true,
18508
+ multiplicity: 0
18509
+ }
18248
18510
  };
18249
18511
  var LOCAL_WALLET_CONFIG = {
18250
18512
  ...BASE_CONFIG,
@@ -19901,6 +20163,7 @@ function getTransferPackageSigningPayload(transferID, transferPackage) {
19901
20163
 
19902
20164
  // src/utils/transaction.ts
19903
20165
  init_buffer();
20166
+ var import_utils11 = require("@noble/hashes/utils");
19904
20167
  var import_btc_signer = require("@scure/btc-signer");
19905
20168
  var INITIAL_TIMELOCK = 2e3;
19906
20169
  var TEST_UNILATERAL_TIMELOCK = 100;
@@ -19909,9 +20172,9 @@ var DIRECT_TIMELOCK_OFFSET = 50;
19909
20172
  var HTLC_TIMELOCK_OFFSET = 70;
19910
20173
  var DIRECT_HTLC_TIMELOCK_OFFSET = 85;
19911
20174
  var INITIAL_SEQUENCE = 1 << 30 | INITIAL_TIMELOCK;
19912
- var INITIAL_DIRECT_SEQUENCE = 1 << 30 | INITIAL_TIMELOCK + DIRECT_TIMELOCK_OFFSET;
19913
20175
  var TEST_UNILATERAL_SEQUENCE = 1 << 30 | TEST_UNILATERAL_TIMELOCK;
19914
20176
  var TEST_UNILATERAL_DIRECT_SEQUENCE = 1 << 30 | TEST_UNILATERAL_TIMELOCK + DIRECT_TIMELOCK_OFFSET;
20177
+ var INITIAL_ROOT_NODE_SEQUENCE = 1 << 30 | 0;
19915
20178
  var ESTIMATED_TX_SIZE = 191;
19916
20179
  var DEFAULT_SATS_PER_VBYTE = 5;
19917
20180
  var DEFAULT_FEE_SATS = ESTIMATED_TX_SIZE * DEFAULT_SATS_PER_VBYTE;
@@ -19921,63 +20184,8 @@ function maybeApplyFee(amount) {
19921
20184
  }
19922
20185
  return amount;
19923
20186
  }
19924
- function createRootTx(depositOutPoint, depositTxOut) {
19925
- const cpfpRootTx = new import_btc_signer.Transaction({
19926
- version: 3,
19927
- allowUnknownOutputs: true
19928
- });
19929
- cpfpRootTx.addInput(depositOutPoint);
19930
- cpfpRootTx.addOutput(depositTxOut);
19931
- cpfpRootTx.addOutput(getEphemeralAnchorOutput());
19932
- const directRootTx = new import_btc_signer.Transaction({
19933
- version: 3,
19934
- allowUnknownOutputs: true
19935
- });
19936
- directRootTx.addInput(depositOutPoint);
19937
- directRootTx.addOutput({
19938
- script: depositTxOut.script,
19939
- amount: maybeApplyFee(depositTxOut.amount ?? 0n)
19940
- });
19941
- return [cpfpRootTx, directRootTx];
19942
- }
19943
- function createSplitTx(parentOutPoint, childTxOuts) {
19944
- const cpfpSplitTx = new import_btc_signer.Transaction({
19945
- version: 3,
19946
- allowUnknownOutputs: true
19947
- });
19948
- cpfpSplitTx.addInput(parentOutPoint);
19949
- for (const txOut of childTxOuts) {
19950
- cpfpSplitTx.addOutput(txOut);
19951
- }
19952
- cpfpSplitTx.addOutput(getEphemeralAnchorOutput());
19953
- const directSplitTx = new import_btc_signer.Transaction({
19954
- version: 3,
19955
- allowUnknownOutputs: true
19956
- });
19957
- directSplitTx.addInput(parentOutPoint);
19958
- let totalOutputAmount = 0n;
19959
- for (const txOut of childTxOuts) {
19960
- totalOutputAmount += txOut.amount ?? 0n;
19961
- }
19962
- if (totalOutputAmount > BigInt(DEFAULT_FEE_SATS)) {
19963
- const feeRatio = Number(DEFAULT_FEE_SATS) / Number(totalOutputAmount);
19964
- for (const txOut of childTxOuts) {
19965
- const adjustedAmount = BigInt(
19966
- Math.floor(Number(txOut.amount ?? 0n) * (1 - feeRatio))
19967
- );
19968
- directSplitTx.addOutput({
19969
- script: txOut.script,
19970
- amount: adjustedAmount
19971
- });
19972
- }
19973
- } else {
19974
- for (const txOut of childTxOuts) {
19975
- directSplitTx.addOutput(txOut);
19976
- }
19977
- }
19978
- return [cpfpSplitTx, directSplitTx];
19979
- }
19980
20187
  function createNodeTx({
20188
+ sequence,
19981
20189
  txOut,
19982
20190
  parentOutPoint,
19983
20191
  applyFee,
@@ -19987,7 +20195,10 @@ function createNodeTx({
19987
20195
  version: 3,
19988
20196
  allowUnknownOutputs: true
19989
20197
  });
19990
- nodeTx.addInput(parentOutPoint);
20198
+ nodeTx.addInput({
20199
+ ...parentOutPoint,
20200
+ sequence
20201
+ });
19991
20202
  if (applyFee) {
19992
20203
  nodeTx.addOutput({
19993
20204
  script: txOut.script,
@@ -20001,52 +20212,92 @@ function createNodeTx({
20001
20212
  }
20002
20213
  return nodeTx;
20003
20214
  }
20004
- function createNodeTxs(txOut, txIn, directTxIn) {
20005
- const cpfpNodeTx = createNodeTx({
20006
- txOut,
20007
- parentOutPoint: txIn,
20008
- includeAnchor: true
20009
- });
20010
- let directNodeTx;
20011
- if (directTxIn) {
20012
- directNodeTx = createNodeTx({
20013
- txOut,
20014
- parentOutPoint: directTxIn,
20015
- includeAnchor: false,
20016
- applyFee: true
20215
+ function createNodeTxs({
20216
+ parentTx,
20217
+ sequence,
20218
+ directSequence,
20219
+ vout
20220
+ }) {
20221
+ const parentOutput = parentTx.getOutput(vout);
20222
+ if (!parentOutput.amount || !parentOutput.script) {
20223
+ throw new ValidationError("Parent output amount or script not found", {
20224
+ field: "parentOutput",
20225
+ value: parentOutput
20017
20226
  });
20018
20227
  }
20019
- return { cpfpNodeTx, directNodeTx };
20228
+ const output = {
20229
+ script: parentOutput.script,
20230
+ amount: parentOutput.amount
20231
+ };
20232
+ const input = {
20233
+ txid: (0, import_utils11.hexToBytes)(getTxId(parentTx)),
20234
+ index: vout
20235
+ };
20236
+ const nodeTx = createNodeTx({
20237
+ sequence,
20238
+ txOut: output,
20239
+ parentOutPoint: input,
20240
+ includeAnchor: true
20241
+ });
20242
+ const directNodeTx = createNodeTx({
20243
+ sequence: directSequence ?? sequence + DIRECT_TIMELOCK_OFFSET,
20244
+ txOut: output,
20245
+ parentOutPoint: input,
20246
+ includeAnchor: false,
20247
+ applyFee: true
20248
+ });
20249
+ return { nodeTx, directNodeTx };
20020
20250
  }
20021
- function createLeafNodeTx(sequence, directSequence, parentOutPoint, txOut, shouldCalculateFee) {
20022
- const cpfpLeafTx = new import_btc_signer.Transaction({
20023
- version: 3,
20024
- allowUnknownOutputs: true
20251
+ function createRootNodeTx(parentTx, vout) {
20252
+ return createNodeTxs({
20253
+ parentTx,
20254
+ sequence: INITIAL_ROOT_NODE_SEQUENCE,
20255
+ vout
20025
20256
  });
20026
- cpfpLeafTx.addInput({
20027
- ...parentOutPoint,
20028
- sequence
20257
+ }
20258
+ function createZeroTimelockNodeTx(parentTx) {
20259
+ return createNodeTxs({
20260
+ parentTx,
20261
+ sequence: INITIAL_ROOT_NODE_SEQUENCE,
20262
+ directSequence: DIRECT_TIMELOCK_OFFSET,
20263
+ vout: 0
20029
20264
  });
20030
- cpfpLeafTx.addOutput(txOut);
20031
- cpfpLeafTx.addOutput(getEphemeralAnchorOutput());
20032
- const directLeafTx = new import_btc_signer.Transaction({
20033
- version: 3,
20034
- allowUnknownOutputs: true
20265
+ }
20266
+ function createInitialTimelockNodeTx(parentTx) {
20267
+ return createNodeTxs({
20268
+ parentTx,
20269
+ sequence: INITIAL_SEQUENCE,
20270
+ vout: 0
20035
20271
  });
20036
- directLeafTx.addInput({
20037
- ...parentOutPoint,
20038
- sequence: directSequence
20272
+ }
20273
+ function createDecrementedTimelockNodeTx(parentTx, currentTx) {
20274
+ const currentSequence = currentTx.getInput(0).sequence;
20275
+ if (!currentSequence) {
20276
+ throw new ValidationError("Current sequence not found", {
20277
+ field: "currentSequence",
20278
+ value: currentSequence
20279
+ });
20280
+ }
20281
+ return createNodeTxs({
20282
+ parentTx,
20283
+ sequence: getNextTransactionSequence(currentSequence).nextSequence,
20284
+ vout: 0
20039
20285
  });
20040
- const amountSats = txOut.amount ?? 0n;
20041
- let outputAmount = amountSats;
20042
- if (shouldCalculateFee) {
20043
- outputAmount = maybeApplyFee(amountSats);
20286
+ }
20287
+ function createTestUnilateralTimelockNodeTx(parentTx, nodeTx) {
20288
+ const sequence = nodeTx.getInput(0).sequence;
20289
+ if (!sequence) {
20290
+ throw new ValidationError("Sequence not found", {
20291
+ field: "sequence",
20292
+ value: sequence
20293
+ });
20044
20294
  }
20045
- directLeafTx.addOutput({
20046
- script: txOut.script,
20047
- amount: outputAmount
20295
+ const isBit30Defined = (sequence || 0) & 1 << 30;
20296
+ return createNodeTxs({
20297
+ parentTx,
20298
+ sequence: isBit30Defined | TEST_UNILATERAL_TIMELOCK,
20299
+ vout: 0
20048
20300
  });
20049
- return [cpfpLeafTx, directLeafTx];
20050
20301
  }
20051
20302
  function createRefundTx({
20052
20303
  sequence,
@@ -20102,110 +20353,109 @@ function getNextHTLCTransactionSequence(currSequence, isNodeTx) {
20102
20353
  };
20103
20354
  }
20104
20355
  function createRefundTxs({
20105
- sequence,
20106
- directSequence,
20107
- input,
20108
- directInput,
20109
- amountSats,
20356
+ nodeTx,
20357
+ directNodeTx,
20110
20358
  receivingPubkey,
20111
- network
20359
+ network,
20360
+ sequence
20112
20361
  }) {
20113
- const cpfpRefundTx = createRefundTx({
20114
- sequence,
20115
- input,
20116
- amountSats,
20117
- receivingPubkey,
20118
- network,
20119
- shouldCalculateFee: false,
20120
- includeAnchor: true
20121
- });
20362
+ const refundInput = {
20363
+ txid: (0, import_utils11.hexToBytes)(getTxId(nodeTx)),
20364
+ index: 0
20365
+ };
20366
+ const nodeAmountSats = nodeTx.getOutput(0).amount;
20367
+ if (nodeAmountSats === void 0) {
20368
+ throw new ValidationError("Node amount not found", {
20369
+ field: "nodeAmountSats",
20370
+ value: nodeAmountSats
20371
+ });
20372
+ }
20122
20373
  let directRefundTx;
20123
- let directFromCpfpRefundTx;
20124
- if (directSequence && directInput) {
20374
+ if (directNodeTx) {
20375
+ const directRefundInput = {
20376
+ txid: (0, import_utils11.hexToBytes)(getTxId(directNodeTx)),
20377
+ index: 0
20378
+ };
20379
+ const directAmountSats = directNodeTx.getOutput(0).amount;
20380
+ if (directAmountSats === void 0) {
20381
+ throw new ValidationError("Direct amount not found", {
20382
+ field: "directAmountSats",
20383
+ value: directAmountSats
20384
+ });
20385
+ }
20125
20386
  directRefundTx = createRefundTx({
20126
- sequence: directSequence,
20127
- input: directInput,
20128
- amountSats,
20129
- receivingPubkey,
20130
- network,
20131
- shouldCalculateFee: true,
20132
- includeAnchor: false
20133
- });
20134
- directFromCpfpRefundTx = createRefundTx({
20135
- sequence: directSequence,
20136
- input,
20137
- amountSats,
20387
+ sequence: sequence + DIRECT_TIMELOCK_OFFSET,
20388
+ input: directRefundInput,
20389
+ amountSats: directAmountSats,
20138
20390
  receivingPubkey,
20139
20391
  network,
20140
20392
  shouldCalculateFee: true,
20141
20393
  includeAnchor: false
20142
20394
  });
20143
- } else if (directInput && !directSequence) {
20144
- throw new ValidationError(
20145
- "directSequence must be provided if directInput is",
20146
- {
20147
- field: "directSequence",
20148
- value: directSequence
20149
- }
20150
- );
20151
20395
  }
20152
- return { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx };
20153
- }
20154
- function createConnectorRefundTransactions(sequence, cpfpNodeOutPoint, directNodeOutPoint, connectorOutput, amountSats, receiverPubKey, network, shouldCalculateFee) {
20155
- const cpfpRefundTx = new import_btc_signer.Transaction({
20156
- version: 3,
20157
- allowUnknownOutputs: true
20396
+ const cpfpRefundTx = createRefundTx({
20397
+ sequence,
20398
+ input: refundInput,
20399
+ amountSats: nodeAmountSats,
20400
+ receivingPubkey,
20401
+ network,
20402
+ shouldCalculateFee: false,
20403
+ includeAnchor: true
20158
20404
  });
20159
- cpfpRefundTx.addInput({
20160
- ...cpfpNodeOutPoint,
20161
- sequence
20405
+ const directFromCpfpRefundTx = createRefundTx({
20406
+ sequence: sequence + DIRECT_TIMELOCK_OFFSET,
20407
+ input: refundInput,
20408
+ amountSats: nodeAmountSats,
20409
+ receivingPubkey,
20410
+ network,
20411
+ shouldCalculateFee: true,
20412
+ includeAnchor: false
20162
20413
  });
20163
- cpfpRefundTx.addInput(connectorOutput);
20164
- const receiverScript = getP2TRScriptFromPublicKey(receiverPubKey, network);
20165
- cpfpRefundTx.addOutput({
20166
- script: receiverScript,
20167
- amount: amountSats
20414
+ return { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx };
20415
+ }
20416
+ function createInitialTimelockRefundTxs(params) {
20417
+ return createRefundTxs({
20418
+ ...params,
20419
+ sequence: INITIAL_SEQUENCE
20168
20420
  });
20169
- const directRefundTx = new import_btc_signer.Transaction({
20170
- version: 3,
20171
- allowUnknownOutputs: true
20421
+ }
20422
+ function createDecrementedTimelockRefundTxs(params) {
20423
+ const nextSequence = getNextTransactionSequence(params.sequence).nextSequence;
20424
+ return createRefundTxs({
20425
+ ...params,
20426
+ sequence: nextSequence
20172
20427
  });
20173
- directRefundTx.addInput({
20174
- ...directNodeOutPoint,
20175
- sequence
20428
+ }
20429
+ function createCurrentTimelockRefundTxs(params) {
20430
+ return createRefundTxs(params);
20431
+ }
20432
+ function createTestUnilateralRefundTxs(params) {
20433
+ return createRefundTxs({
20434
+ ...params,
20435
+ sequence: TEST_UNILATERAL_SEQUENCE
20176
20436
  });
20177
- directRefundTx.addInput(connectorOutput);
20178
- let outputAmount = amountSats;
20179
- if (shouldCalculateFee) {
20180
- outputAmount = maybeApplyFee(amountSats);
20437
+ }
20438
+ function createConnectorRefundTxs(params) {
20439
+ const { connectorOutput, ...baseParams } = params;
20440
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createDecrementedTimelockRefundTxs(baseParams);
20441
+ cpfpRefundTx.addInput(connectorOutput);
20442
+ if (directRefundTx) {
20443
+ directRefundTx.addInput(connectorOutput);
20181
20444
  }
20182
- directRefundTx.addOutput({
20183
- script: receiverScript,
20184
- amount: outputAmount
20185
- });
20186
- const directFromCpfpTx = new import_btc_signer.Transaction({
20187
- version: 3,
20188
- allowUnknownOutputs: true
20189
- });
20190
- directFromCpfpTx.addInput({
20191
- ...cpfpNodeOutPoint,
20192
- sequence
20193
- });
20194
- directFromCpfpTx.addInput(connectorOutput);
20195
- directFromCpfpTx.addOutput({
20196
- script: receiverScript,
20197
- amount: outputAmount
20198
- });
20199
- return [cpfpRefundTx, directRefundTx, directFromCpfpTx];
20445
+ if (directFromCpfpRefundTx) {
20446
+ directFromCpfpRefundTx.addInput(connectorOutput);
20447
+ }
20448
+ return { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx };
20200
20449
  }
20201
20450
  function getCurrentTimelock(currSequence) {
20202
20451
  return (currSequence || 0) & 65535;
20203
20452
  }
20204
20453
  function getTransactionSequence(currSequence) {
20205
20454
  const timelock = getCurrentTimelock(currSequence);
20455
+ const isBit30Defined = (currSequence || 0) & 1 << 30;
20206
20456
  return {
20207
- nextSequence: 1 << 30 | timelock,
20208
- nextDirectSequence: 1 << 30 | timelock + DIRECT_TIMELOCK_OFFSET
20457
+ nextSequence: isBit30Defined | timelock,
20458
+ nextDirectSequence: isBit30Defined | timelock + DIRECT_TIMELOCK_OFFSET
20209
20459
  };
20210
20460
  }
20211
20461
  function checkIfValidSequence(currSequence) {
@@ -20224,6 +20474,13 @@ function checkIfValidSequence(currSequence) {
20224
20474
  });
20225
20475
  }
20226
20476
  }
20477
+ function isZeroTimelock(currSequence) {
20478
+ return getCurrentTimelock(currSequence) === 0;
20479
+ }
20480
+ function doesTxnNeedRenewed(currSequence) {
20481
+ const currentTimelock = getCurrentTimelock(currSequence);
20482
+ return currentTimelock <= 100;
20483
+ }
20227
20484
  function doesLeafNeedRefresh(currSequence, isNodeTx) {
20228
20485
  const currentTimelock = getCurrentTimelock(currSequence);
20229
20486
  if (isNodeTx) {
@@ -20234,6 +20491,7 @@ function doesLeafNeedRefresh(currSequence, isNodeTx) {
20234
20491
  function getNextTransactionSequence(currSequence, isNodeTx) {
20235
20492
  const currentTimelock = getCurrentTimelock(currSequence);
20236
20493
  const nextTimelock = currentTimelock - TIME_LOCK_INTERVAL;
20494
+ const isBit30Defined = (currSequence || 0) & 1 << 30;
20237
20495
  if (isNodeTx && nextTimelock < 0) {
20238
20496
  throw new ValidationError("timelock interval is less than 0", {
20239
20497
  field: "nextTimelock",
@@ -20248,8 +20506,8 @@ function getNextTransactionSequence(currSequence, isNodeTx) {
20248
20506
  });
20249
20507
  }
20250
20508
  return {
20251
- nextSequence: 1 << 30 | nextTimelock,
20252
- nextDirectSequence: 1 << 30 | nextTimelock + DIRECT_TIMELOCK_OFFSET
20509
+ nextSequence: isBit30Defined | nextTimelock,
20510
+ nextDirectSequence: isBit30Defined | nextTimelock + DIRECT_TIMELOCK_OFFSET
20253
20511
  };
20254
20512
  }
20255
20513
  function getEphemeralAnchorOutput() {
@@ -20262,7 +20520,7 @@ function getEphemeralAnchorOutput() {
20262
20520
 
20263
20521
  // src/utils/unilateral-exit.ts
20264
20522
  init_buffer();
20265
- var import_utils11 = require("@noble/curves/utils");
20523
+ var import_utils12 = require("@noble/curves/utils");
20266
20524
  var import_legacy = require("@noble/hashes/legacy");
20267
20525
  var import_sha25 = require("@noble/hashes/sha2");
20268
20526
  var btc3 = __toESM(require("@scure/btc-signer"), 1);
@@ -20277,7 +20535,7 @@ function isEphemeralAnchorOutput(script, amount) {
20277
20535
  }
20278
20536
  async function constructUnilateralExitTxs(nodeHexStrings, sparkClient, network) {
20279
20537
  const result = [];
20280
- const nodes = nodeHexStrings.map((hex) => TreeNode.decode((0, import_utils11.hexToBytes)(hex)));
20538
+ const nodes = nodeHexStrings.map((hex) => TreeNode.decode((0, import_utils12.hexToBytes)(hex)));
20281
20539
  const nodeMap = /* @__PURE__ */ new Map();
20282
20540
  for (const node of nodes) {
20283
20541
  nodeMap.set(node.id, node);
@@ -20333,10 +20591,10 @@ async function constructUnilateralExitTxs(nodeHexStrings, sparkClient, network)
20333
20591
  }
20334
20592
  }
20335
20593
  for (const chainNode of chain) {
20336
- const nodeTx = (0, import_utils11.bytesToHex)(chainNode.nodeTx);
20594
+ const nodeTx = (0, import_utils12.bytesToHex)(chainNode.nodeTx);
20337
20595
  transactions.push(nodeTx);
20338
20596
  if (chainNode.id === node.id) {
20339
- const refundTx = (0, import_utils11.bytesToHex)(chainNode.refundTx);
20597
+ const refundTx = (0, import_utils12.bytesToHex)(chainNode.refundTx);
20340
20598
  transactions.push(refundTx);
20341
20599
  }
20342
20600
  }
@@ -20366,7 +20624,7 @@ async function constructUnilateralExitFeeBumpPackages(nodeHexStrings, utxos, fee
20366
20624
  `Node hex string at index ${i} appears to be a raw transaction hex, not a TreeNode protobuf. Use 'leafidtohex' command to convert node IDs to proper hex strings.`
20367
20625
  );
20368
20626
  }
20369
- const nodeBytes = (0, import_utils11.hexToBytes)(hex);
20627
+ const nodeBytes = (0, import_utils12.hexToBytes)(hex);
20370
20628
  const node = TreeNode.decode(nodeBytes);
20371
20629
  if (!node.id) {
20372
20630
  throw new Error(
@@ -20442,7 +20700,7 @@ async function constructUnilateralExitFeeBumpPackages(nodeHexStrings, utxos, fee
20442
20700
  }
20443
20701
  }
20444
20702
  for (const chainNode of chain) {
20445
- let nodeTxHex = (0, import_utils11.bytesToHex)(chainNode.nodeTx);
20703
+ let nodeTxHex = (0, import_utils12.bytesToHex)(chainNode.nodeTx);
20446
20704
  try {
20447
20705
  const txObj = getTxFromRawTxHex(nodeTxHex);
20448
20706
  const txid = getTxId(txObj);
@@ -20459,7 +20717,7 @@ async function constructUnilateralExitFeeBumpPackages(nodeHexStrings, utxos, fee
20459
20717
  for (let i = txObj.outputsLength - 1; i >= 0; i--) {
20460
20718
  const output = txObj.getOutput(i);
20461
20719
  if (output?.amount === 0n && output.script) {
20462
- anchorOutputScriptHex = (0, import_utils11.bytesToHex)(output.script);
20720
+ anchorOutputScriptHex = (0, import_utils12.bytesToHex)(output.script);
20463
20721
  break;
20464
20722
  }
20465
20723
  }
@@ -20480,11 +20738,11 @@ async function constructUnilateralExitFeeBumpPackages(nodeHexStrings, utxos, fee
20480
20738
  usedUtxos,
20481
20739
  correctedParentTx
20482
20740
  } = constructFeeBumpTx(nodeTxHex, availableUtxos, feeRate, void 0);
20483
- const feeBumpTx = btc3.Transaction.fromPSBT((0, import_utils11.hexToBytes)(nodeFeeBumpPsbt));
20741
+ const feeBumpTx = btc3.Transaction.fromPSBT((0, import_utils12.hexToBytes)(nodeFeeBumpPsbt));
20484
20742
  var feeBumpOut = feeBumpTx.outputsLength === 1 ? feeBumpTx.getOutput(0) : null;
20485
20743
  var feeBumpOutPubKey = null;
20486
20744
  for (const usedUtxo of usedUtxos) {
20487
- if (feeBumpOut && (0, import_utils11.bytesToHex)(feeBumpOut.script) == usedUtxo.script) {
20745
+ if (feeBumpOut && (0, import_utils12.bytesToHex)(feeBumpOut.script) == usedUtxo.script) {
20488
20746
  feeBumpOutPubKey = usedUtxo.publicKey;
20489
20747
  }
20490
20748
  const index = availableUtxos.findIndex(
@@ -20499,20 +20757,20 @@ async function constructUnilateralExitFeeBumpPackages(nodeHexStrings, utxos, fee
20499
20757
  txid: getTxId(feeBumpTx),
20500
20758
  vout: 0,
20501
20759
  value: feeBumpOut.amount,
20502
- script: (0, import_utils11.bytesToHex)(feeBumpOut.script),
20760
+ script: (0, import_utils12.bytesToHex)(feeBumpOut.script),
20503
20761
  publicKey: feeBumpOutPubKey
20504
20762
  });
20505
20763
  const finalNodeTx = correctedParentTx || nodeTxHex;
20506
20764
  txPackages.push({ tx: finalNodeTx, feeBumpPsbt: nodeFeeBumpPsbt });
20507
20765
  if (chainNode.id === node.id) {
20508
- let refundTxHex = (0, import_utils11.bytesToHex)(chainNode.refundTx);
20766
+ let refundTxHex = (0, import_utils12.bytesToHex)(chainNode.refundTx);
20509
20767
  try {
20510
20768
  const txObj = getTxFromRawTxHex(refundTxHex);
20511
20769
  let anchorOutputScriptHex;
20512
20770
  for (let i = txObj.outputsLength - 1; i >= 0; i--) {
20513
20771
  const output = txObj.getOutput(i);
20514
20772
  if (output?.amount === 0n && output.script) {
20515
- anchorOutputScriptHex = (0, import_utils11.bytesToHex)(output.script);
20773
+ anchorOutputScriptHex = (0, import_utils12.bytesToHex)(output.script);
20516
20774
  break;
20517
20775
  }
20518
20776
  }
@@ -20535,12 +20793,12 @@ async function constructUnilateralExitFeeBumpPackages(nodeHexStrings, utxos, fee
20535
20793
  void 0
20536
20794
  );
20537
20795
  const feeBumpTx2 = btc3.Transaction.fromPSBT(
20538
- (0, import_utils11.hexToBytes)(refundFeeBump.feeBumpPsbt)
20796
+ (0, import_utils12.hexToBytes)(refundFeeBump.feeBumpPsbt)
20539
20797
  );
20540
20798
  var feeBumpOut = feeBumpTx2.outputsLength === 1 ? feeBumpTx2.getOutput(0) : null;
20541
20799
  var feeBumpOutPubKey = null;
20542
20800
  for (const usedUtxo of usedUtxos) {
20543
- if (feeBumpOut && (0, import_utils11.bytesToHex)(feeBumpOut.script) == usedUtxo.script) {
20801
+ if (feeBumpOut && (0, import_utils12.bytesToHex)(feeBumpOut.script) == usedUtxo.script) {
20544
20802
  feeBumpOutPubKey = usedUtxo.publicKey;
20545
20803
  }
20546
20804
  const index = availableUtxos.findIndex(
@@ -20555,7 +20813,7 @@ async function constructUnilateralExitFeeBumpPackages(nodeHexStrings, utxos, fee
20555
20813
  txid: getTxId(feeBumpTx2),
20556
20814
  vout: 0,
20557
20815
  value: feeBumpOut.amount,
20558
- script: (0, import_utils11.bytesToHex)(feeBumpOut.script),
20816
+ script: (0, import_utils12.bytesToHex)(feeBumpOut.script),
20559
20817
  publicKey: feeBumpOutPubKey
20560
20818
  });
20561
20819
  txPackages.push({
@@ -20650,10 +20908,10 @@ function constructFeeBumpTx(txHex, utxos, feeRate, previousFeeBumpTx) {
20650
20908
  if (!fundingUtxo) {
20651
20909
  throw new Error(`UTXO at index ${i} is undefined`);
20652
20910
  }
20653
- const pubKeyHash = hash160((0, import_utils11.hexToBytes)(fundingUtxo.publicKey));
20911
+ const pubKeyHash = hash160((0, import_utils12.hexToBytes)(fundingUtxo.publicKey));
20654
20912
  const scriptToUse = new Uint8Array([0, 20, ...pubKeyHash]);
20655
- const providedScript = (0, import_utils11.hexToBytes)(fundingUtxo.script);
20656
- if ((0, import_utils11.bytesToHex)(scriptToUse) !== (0, import_utils11.bytesToHex)(providedScript)) {
20913
+ const providedScript = (0, import_utils12.hexToBytes)(fundingUtxo.script);
20914
+ if ((0, import_utils12.bytesToHex)(scriptToUse) !== (0, import_utils12.bytesToHex)(providedScript)) {
20657
20915
  throw new Error(
20658
20916
  `\u274C Derived script doesn't match provided script for UTXO ${i + 1}.`
20659
20917
  );
@@ -20718,7 +20976,7 @@ function constructFeeBumpTx(txHex, utxos, feeRate, previousFeeBumpTx) {
20718
20976
  }
20719
20977
  let psbtHex;
20720
20978
  try {
20721
- psbtHex = (0, import_utils11.bytesToHex)(builder.toPSBT());
20979
+ psbtHex = (0, import_utils12.bytesToHex)(builder.toPSBT());
20722
20980
  } catch (error) {
20723
20981
  throw new Error(`Failed to extract transaction: ${error}`);
20724
20982
  }
@@ -20767,12 +21025,12 @@ init_logging();
20767
21025
  init_buffer();
20768
21026
  var import_secp256k17 = require("@bitcoinerlab/secp256k1");
20769
21027
  var import_secp256k18 = require("@noble/curves/secp256k1");
20770
- var import_utils13 = require("@noble/curves/utils");
21028
+ var import_utils14 = require("@noble/curves/utils");
20771
21029
  var import_sha26 = require("@noble/hashes/sha2");
20772
21030
  var import_bip32 = require("@scure/bip32");
20773
21031
  var import_bip39 = require("@scure/bip39");
20774
21032
  var import_english = require("@scure/bip39/wordlists/english");
20775
- var import_utils14 = require("@scure/btc-signer/utils");
21033
+ var import_utils15 = require("@scure/btc-signer/utils");
20776
21034
  var ecies = __toESM(require("eciesjs"), 1);
20777
21035
 
20778
21036
  // src/constants.ts
@@ -20785,7 +21043,7 @@ var isWebExtension = (
20785
21043
  "chrome" in globalThis && globalThis.chrome.runtime?.id
20786
21044
  );
20787
21045
  var userAgent = "navigator" in globalThis ? globalThis.navigator.userAgent || "unknown-user-agent" : void 0;
20788
- var packageVersion = true ? "0.3.8" : "unknown";
21046
+ var packageVersion = true ? "0.4.0" : "unknown";
20789
21047
  var baseEnvStr = "unknown";
20790
21048
  if (isBun) {
20791
21049
  const bunVersion = "version" in globalThis.Bun ? globalThis.Bun.version : "unknown-version";
@@ -20886,7 +21144,7 @@ var TaprootOutputKeysGenerator = class {
20886
21144
  }
20887
21145
  const derivationPath = this.useAddressIndex ? `m/86'/0'/0'/0/${accountNumber}` : `m/86'/0'/${accountNumber}'/0/0`;
20888
21146
  const taprootInternalKey = hdkey.derive(derivationPath);
20889
- let tweakedPrivateKey = (0, import_utils14.taprootTweakPrivKey)(taprootInternalKey.privateKey);
21147
+ let tweakedPrivateKey = (0, import_utils15.taprootTweakPrivKey)(taprootInternalKey.privateKey);
20890
21148
  let tweakedPublicKey = import_secp256k18.secp256k1.getPublicKey(tweakedPrivateKey);
20891
21149
  if (tweakedPublicKey[0] === 3) {
20892
21150
  tweakedPrivateKey = (0, import_secp256k17.privateNegate)(tweakedPrivateKey);
@@ -20964,7 +21222,7 @@ var DefaultSparkSigner = class {
20964
21222
  });
20965
21223
  }
20966
21224
  const receiverEciesPrivKey = ecies.PrivateKey.fromHex(
20967
- (0, import_utils13.bytesToHex)(this.identityKey.privateKey)
21225
+ (0, import_utils14.bytesToHex)(this.identityKey.privateKey)
20968
21226
  );
20969
21227
  const privateKey = ecies.decrypt(receiverEciesPrivKey.toHex(), ciphertext);
20970
21228
  return privateKey;
@@ -21098,7 +21356,7 @@ var DefaultSparkSigner = class {
21098
21356
  threshold,
21099
21357
  numShares
21100
21358
  }) {
21101
- const secretAsInt = (0, import_utils13.bytesToNumberBE)(secret);
21359
+ const secretAsInt = (0, import_utils14.bytesToNumberBE)(secret);
21102
21360
  return splitSecretWithProofs(secretAsInt, curveOrder, threshold, numShares);
21103
21361
  }
21104
21362
  async signFrost({
@@ -21134,7 +21392,7 @@ var DefaultSparkSigner = class {
21134
21392
  publicKey,
21135
21393
  verifyingKey
21136
21394
  };
21137
- const logMessage = (0, import_utils13.bytesToHex)(message);
21395
+ const logMessage = (0, import_utils14.bytesToHex)(message);
21138
21396
  const result = await SparkFrost.signFrost({
21139
21397
  message,
21140
21398
  keyPackage,
@@ -21176,7 +21434,7 @@ var DefaultSparkSigner = class {
21176
21434
  }
21177
21435
  async createSparkWalletFromSeed(seed, accountNumber) {
21178
21436
  if (typeof seed === "string") {
21179
- seed = (0, import_utils13.hexToBytes)(seed);
21437
+ seed = (0, import_utils14.hexToBytes)(seed);
21180
21438
  }
21181
21439
  const {
21182
21440
  identityKey,
@@ -21188,7 +21446,7 @@ var DefaultSparkSigner = class {
21188
21446
  this.depositKey = depositKey;
21189
21447
  this.signingKey = signingKey.hdKey;
21190
21448
  this.staticDepositKey = staticDepositKey.hdKey;
21191
- return (0, import_utils13.bytesToHex)(identityKey.publicKey);
21449
+ return (0, import_utils14.bytesToHex)(identityKey.publicKey);
21192
21450
  }
21193
21451
  async signMessageWithIdentityKey(message, compact) {
21194
21452
  if (!this.identityKey?.privateKey) {
@@ -21209,7 +21467,7 @@ var DefaultSparkSigner = class {
21209
21467
  });
21210
21468
  }
21211
21469
  const receiverEciesPrivKey = ecies.PrivateKey.fromHex(
21212
- (0, import_utils13.bytesToHex)(this.identityKey.privateKey)
21470
+ (0, import_utils14.bytesToHex)(this.identityKey.privateKey)
21213
21471
  );
21214
21472
  const privateKey = ecies.decrypt(receiverEciesPrivKey.toHex(), ciphertext);
21215
21473
  const publicKey = import_secp256k18.secp256k1.getPublicKey(privateKey);
@@ -21233,15 +21491,15 @@ var DefaultSparkSigner = class {
21233
21491
  }
21234
21492
  signTransactionIndex(tx, index, publicKey) {
21235
21493
  let privateKey;
21236
- if ((0, import_utils13.equalBytes)(publicKey, this.identityKey?.publicKey ?? new Uint8Array())) {
21494
+ if ((0, import_utils14.equalBytes)(publicKey, this.identityKey?.publicKey ?? new Uint8Array())) {
21237
21495
  privateKey = this.identityKey?.privateKey;
21238
- } else if ((0, import_utils13.equalBytes)(publicKey, this.depositKey?.publicKey ?? new Uint8Array())) {
21496
+ } else if ((0, import_utils14.equalBytes)(publicKey, this.depositKey?.publicKey ?? new Uint8Array())) {
21239
21497
  privateKey = this.depositKey?.privateKey;
21240
21498
  }
21241
21499
  if (!privateKey) {
21242
21500
  throw new ValidationError("Private key not found for public key", {
21243
21501
  field: "privateKey",
21244
- value: (0, import_utils13.bytesToHex)(publicKey)
21502
+ value: (0, import_utils14.bytesToHex)(publicKey)
21245
21503
  });
21246
21504
  }
21247
21505
  tx.signIdx(privateKey, index);
@@ -21323,7 +21581,7 @@ var import_secp256k114 = require("@noble/curves/secp256k1");
21323
21581
  var import_utils25 = require("@noble/curves/utils");
21324
21582
  var import_bip392 = require("@scure/bip39");
21325
21583
  var import_english2 = require("@scure/bip39/wordlists/english");
21326
- var import_btc_signer7 = require("@scure/btc-signer");
21584
+ var import_btc_signer5 = require("@scure/btc-signer");
21327
21585
  var import_async_mutex = require("async-mutex");
21328
21586
  var import_uuidv75 = require("uuidv7");
21329
21587
 
@@ -22570,28 +22828,22 @@ var WalletConfigService = class {
22570
22828
  getEvents() {
22571
22829
  return this.config.events;
22572
22830
  }
22831
+ getOptimizationOptions() {
22832
+ return this.config.optimizationOptions;
22833
+ }
22573
22834
  };
22574
22835
 
22575
22836
  // src/services/coop-exit.ts
22576
22837
  init_buffer();
22577
- var import_btc_signer3 = require("@scure/btc-signer");
22578
22838
  var import_uuidv73 = require("uuidv7");
22579
22839
 
22580
22840
  // src/services/transfer.ts
22581
22841
  init_buffer();
22582
22842
  var import_secp256k19 = require("@noble/curves/secp256k1");
22583
- var import_utils15 = require("@noble/curves/utils");
22843
+ var import_utils16 = require("@noble/curves/utils");
22584
22844
  var import_sha28 = require("@noble/hashes/sha2");
22585
- var import_btc_signer2 = require("@scure/btc-signer");
22586
22845
  var ecies2 = __toESM(require("eciesjs"), 1);
22587
22846
  var import_uuidv72 = require("uuidv7");
22588
- function getSigningJobProto(signingJob) {
22589
- return {
22590
- signingPublicKey: signingJob.signingPublicKey,
22591
- rawTx: signingJob.rawTx,
22592
- signingNonceCommitment: signingJob.signingNonceCommitment.commitment
22593
- };
22594
- }
22595
22847
  var BaseTransferService = class {
22596
22848
  config;
22597
22849
  connectionManager;
@@ -22938,7 +23190,7 @@ var BaseTransferService = class {
22938
23190
  }
22939
23191
  async prepareSendTransferKeyTweaks(transferID, receiverIdentityPubkey, leaves, cpfpRefundSignatureMap, directRefundSignatureMap, directFromCpfpRefundSignatureMap) {
22940
23192
  const receiverEciesPubKey = ecies2.PublicKey.fromHex(
22941
- (0, import_utils15.bytesToHex)(receiverIdentityPubkey)
23193
+ (0, import_utils16.bytesToHex)(receiverIdentityPubkey)
22942
23194
  );
22943
23195
  const leavesTweaksMap = /* @__PURE__ */ new Map();
22944
23196
  for (const leaf of leaves) {
@@ -22979,7 +23231,7 @@ var BaseTransferService = class {
22979
23231
  throw new Error(`Share not found for operator ${operator.id}`);
22980
23232
  }
22981
23233
  const pubkeyTweak = import_secp256k19.secp256k1.getPublicKey(
22982
- (0, import_utils15.numberToBytesBE)(share.share, 32),
23234
+ (0, import_utils16.numberToBytesBE)(share.share, 32),
22983
23235
  true
22984
23236
  );
22985
23237
  pubkeySharesTweak.set(identifier, pubkeyTweak);
@@ -23004,7 +23256,7 @@ var BaseTransferService = class {
23004
23256
  leafTweaksMap.set(identifier, {
23005
23257
  leafId: leaf.leaf.id,
23006
23258
  secretShareTweak: {
23007
- secretShare: (0, import_utils15.numberToBytesBE)(share.share, 32),
23259
+ secretShare: (0, import_utils16.numberToBytesBE)(share.share, 32),
23008
23260
  proofs: share.proofs
23009
23261
  },
23010
23262
  pubkeySharesTweak: Object.fromEntries(pubkeySharesTweak),
@@ -23027,7 +23279,7 @@ var BaseTransferService = class {
23027
23279
  return void 0;
23028
23280
  }
23029
23281
  compareTransfers(transfer1, transfer2) {
23030
- return transfer1.id === transfer2.id && (0, import_utils15.equalBytes)(
23282
+ return transfer1.id === transfer2.id && (0, import_utils16.equalBytes)(
23031
23283
  transfer1.senderIdentityPublicKey,
23032
23284
  transfer2.senderIdentityPublicKey
23033
23285
  ) && transfer1.status === transfer2.status && transfer1.totalValue === transfer2.totalValue && transfer1.expiryTime?.getTime() === transfer2.expiryTime?.getTime() && transfer1.leaves.length === transfer2.leaves.length;
@@ -23273,42 +23525,27 @@ var TransferService = class extends BaseTransferService {
23273
23525
  throw new Error(`Leaf data not found for leaf ${leaf.leaf.id}`);
23274
23526
  }
23275
23527
  const nodeTx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
23276
- const cpfpNodeOutPoint = {
23277
- txid: (0, import_utils15.hexToBytes)(getTxId(nodeTx)),
23278
- index: 0
23279
- };
23280
23528
  let directNodeTx;
23281
- let directNodeOutPoint;
23282
23529
  if (leaf.leaf.directTx.length > 0) {
23283
23530
  directNodeTx = getTxFromRawTxBytes(leaf.leaf.directTx);
23284
- directNodeOutPoint = {
23285
- txid: (0, import_utils15.hexToBytes)(getTxId(directNodeTx)),
23286
- index: 0
23287
- };
23288
23531
  }
23289
23532
  const currRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
23290
- const sequence = currRefundTx.getInput(0).sequence;
23291
- if (!sequence) {
23533
+ const currentSequence = currRefundTx.getInput(0).sequence;
23534
+ if (!currentSequence) {
23292
23535
  throw new ValidationError("Invalid refund transaction", {
23293
23536
  field: "sequence",
23294
23537
  value: currRefundTx.getInput(0),
23295
23538
  expected: "Non-null sequence"
23296
23539
  });
23297
23540
  }
23298
- const { nextSequence, nextDirectSequence } = isForClaim ? getTransactionSequence(sequence) : getNextTransactionSequence(sequence);
23299
- const amountSats = currRefundTx.getOutput(0).amount;
23300
- if (amountSats === void 0) {
23301
- throw new Error("Amount not found in signRefunds");
23302
- }
23303
- const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
23304
- sequence: nextSequence,
23305
- directSequence: nextDirectSequence,
23306
- input: cpfpNodeOutPoint,
23307
- directInput: directNodeOutPoint,
23308
- amountSats,
23541
+ const refundTxsParams = {
23542
+ nodeTx,
23543
+ directNodeTx,
23544
+ sequence: currentSequence,
23309
23545
  receivingPubkey: refundSigningData.receivingPubkey,
23310
23546
  network: this.config.getNetwork()
23311
- });
23547
+ };
23548
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = isForClaim ? createCurrentTimelockRefundTxs(refundTxsParams) : createDecrementedTimelockRefundTxs(refundTxsParams);
23312
23549
  refundSigningData.refundTx = cpfpRefundTx;
23313
23550
  refundSigningData.directRefundTx = directRefundTx;
23314
23551
  refundSigningData.directFromCpfpRefundTx = directFromCpfpRefundTx;
@@ -23416,7 +23653,7 @@ var TransferService = class extends BaseTransferService {
23416
23653
  throw new Error(`Share not found for operator ${operator.id}`);
23417
23654
  }
23418
23655
  const pubkeyTweak = import_secp256k19.secp256k1.getPublicKey(
23419
- (0, import_utils15.numberToBytesBE)(share.share, 32)
23656
+ (0, import_utils16.numberToBytesBE)(share.share, 32)
23420
23657
  );
23421
23658
  pubkeySharesTweak.set(identifier, pubkeyTweak);
23422
23659
  }
@@ -23429,7 +23666,7 @@ var TransferService = class extends BaseTransferService {
23429
23666
  leafTweaksMap.set(identifier, {
23430
23667
  leafId: leaf.leaf.id,
23431
23668
  secretShareTweak: {
23432
- secretShare: (0, import_utils15.numberToBytesBE)(share.share, 32),
23669
+ secretShare: (0, import_utils16.numberToBytesBE)(share.share, 32),
23433
23670
  proofs: share.proofs
23434
23671
  },
23435
23672
  pubkeySharesTweak: Object.fromEntries(pubkeySharesTweak)
@@ -23521,71 +23758,127 @@ var TransferService = class extends BaseTransferService {
23521
23758
  throw new Error(`Error querying pending transfers by sender: ${error}`);
23522
23759
  }
23523
23760
  }
23524
- async refreshTimelockNodesInternal(node, parentNode, useTestUnilateralSequence) {
23525
- const signingJobs = [];
23526
- const parentNodeTx = getTxFromRawTxBytes(parentNode.nodeTx);
23527
- const parentNodeOutput = parentNodeTx.getOutput(0);
23528
- if (!parentNodeOutput) {
23529
- throw Error("Could not get parent node output");
23761
+ async renewRefundTxn(node, parentNode) {
23762
+ const sparkClient = await this.connectionManager.createSparkClient(
23763
+ this.config.getCoordinatorAddress()
23764
+ );
23765
+ const signingJobs = await this.createRenewRefundSigningJobs(
23766
+ node,
23767
+ parentNode
23768
+ );
23769
+ const statechainCommitments = await sparkClient.get_signing_commitments({
23770
+ nodeIds: [node.id],
23771
+ count: signingJobs.length
23772
+ });
23773
+ const mappedSigningJobs = signingJobs.map((signingJob, index) => {
23774
+ const signingNonceCommitments = statechainCommitments.signingCommitments[index]?.signingNonceCommitments;
23775
+ if (!signingNonceCommitments) {
23776
+ throw new Error("Signing nonce commitments not found");
23777
+ }
23778
+ return {
23779
+ ...signingJob,
23780
+ signingNonceCommitments
23781
+ };
23782
+ });
23783
+ const userSignedTxSigningJobs = await this.signingService.signSigningJobs(mappedSigningJobs);
23784
+ const renewRefundTimelockSigningJob = {
23785
+ nodeTxSigningJob: userSignedTxSigningJobs.get("node"),
23786
+ refundTxSigningJob: userSignedTxSigningJobs.get("cpfp"),
23787
+ directNodeTxSigningJob: userSignedTxSigningJobs.get("directNode"),
23788
+ directRefundTxSigningJob: userSignedTxSigningJobs.get("direct"),
23789
+ directFromCpfpRefundTxSigningJob: userSignedTxSigningJobs.get("directFromCpfp")
23790
+ };
23791
+ const response = await sparkClient.renew_leaf({
23792
+ leafId: node.id,
23793
+ signingJobs: {
23794
+ $case: "renewRefundTimelockSigningJob",
23795
+ renewRefundTimelockSigningJob
23796
+ }
23797
+ });
23798
+ if (response.renewResult?.$case !== "renewRefundTimelockResult" || !response.renewResult?.renewRefundTimelockResult.node) {
23799
+ throw new ValidationError("Unexpected renew result", {
23800
+ field: "renewResult",
23801
+ value: response.renewResult
23802
+ });
23530
23803
  }
23531
- const nodeTx = getTxFromRawTxBytes(node.nodeTx);
23532
- const nodeInput = nodeTx.getInput(0);
23533
- const nodeOutput = nodeTx.getOutput(0);
23534
- if (!nodeOutput) {
23535
- throw Error("Could not get node output");
23536
- }
23537
- let directNodeTx;
23538
- let directNodeInput;
23539
- if (node.directTx.length > 0) {
23540
- directNodeTx = getTxFromRawTxBytes(node.directTx);
23541
- directNodeInput = directNodeTx.getInput(0);
23542
- }
23543
- const currSequence = nodeInput.sequence;
23544
- if (!currSequence) {
23545
- throw new ValidationError("Invalid node transaction", {
23546
- field: "sequence",
23547
- value: nodeInput,
23548
- expected: "Non-null sequence"
23804
+ return response.renewResult?.renewRefundTimelockResult.node;
23805
+ }
23806
+ async renewNodeTxn(node, parentNode) {
23807
+ const sparkClient = await this.connectionManager.createSparkClient(
23808
+ this.config.getCoordinatorAddress()
23809
+ );
23810
+ const signingJobs = await this.createRenewNodeSigningJobs(node, parentNode);
23811
+ const statechainCommitments = await sparkClient.get_signing_commitments({
23812
+ nodeIds: [node.id],
23813
+ count: signingJobs.length
23814
+ });
23815
+ const mappedSigningJobs = signingJobs.map((signingJob, index) => {
23816
+ const signingNonceCommitments = statechainCommitments.signingCommitments[index]?.signingNonceCommitments;
23817
+ if (!signingNonceCommitments) {
23818
+ throw new Error("Signing nonce commitments not found");
23819
+ }
23820
+ return {
23821
+ ...signingJob,
23822
+ signingNonceCommitments
23823
+ };
23824
+ });
23825
+ const userSignedTxSigningJobs = await this.signingService.signSigningJobs(mappedSigningJobs);
23826
+ const response = await sparkClient.renew_leaf({
23827
+ leafId: node.id,
23828
+ signingJobs: {
23829
+ $case: "renewNodeTimelockSigningJob",
23830
+ renewNodeTimelockSigningJob: {
23831
+ splitNodeTxSigningJob: userSignedTxSigningJobs.get("split"),
23832
+ splitNodeDirectTxSigningJob: userSignedTxSigningJobs.get("directSplit"),
23833
+ nodeTxSigningJob: userSignedTxSigningJobs.get("node"),
23834
+ directNodeTxSigningJob: userSignedTxSigningJobs.get("directNode"),
23835
+ refundTxSigningJob: userSignedTxSigningJobs.get("cpfp"),
23836
+ directRefundTxSigningJob: userSignedTxSigningJobs.get("direct"),
23837
+ directFromCpfpRefundTxSigningJob: userSignedTxSigningJobs.get("directFromCpfp")
23838
+ }
23839
+ }
23840
+ });
23841
+ if (response.renewResult?.$case !== "renewNodeTimelockResult" || !response.renewResult?.renewNodeTimelockResult.node) {
23842
+ throw new ValidationError("Unexpected renew result", {
23843
+ field: "renewResult",
23844
+ value: response.renewResult
23549
23845
  });
23550
23846
  }
23551
- let { nextSequence, nextDirectSequence } = getNextTransactionSequence(
23552
- currSequence,
23553
- true
23847
+ return response.renewResult.renewNodeTimelockResult.node;
23848
+ }
23849
+ async createRenewRefundSigningJobs(node, parentNode) {
23850
+ const signingJobs = [];
23851
+ const parentTx = getTxFromRawTxBytes(parentNode.nodeTx);
23852
+ const parentNodeOutput = getTxFromRawTxBytes(parentNode.nodeTx).getOutput(
23853
+ 0
23554
23854
  );
23555
- const output = {
23855
+ if (!parentNodeOutput) {
23856
+ throw new Error("Parent node output not found");
23857
+ }
23858
+ const unsignedParentNodeOutput = {
23556
23859
  script: parentNodeOutput.script,
23557
23860
  amount: parentNodeOutput.amount
23558
23861
  };
23559
- const newNodeInput = {
23560
- txid: nodeInput.txid,
23561
- index: nodeInput.index,
23562
- sequence: useTestUnilateralSequence ? TEST_UNILATERAL_SEQUENCE : nextSequence
23563
- };
23564
- const newDirectInput = directNodeTx && directNodeInput ? {
23565
- txid: directNodeInput.txid,
23566
- index: directNodeInput.index,
23567
- sequence: useTestUnilateralSequence ? TEST_UNILATERAL_DIRECT_SEQUENCE : nextDirectSequence
23568
- } : void 0;
23569
- const { cpfpNodeTx, directNodeTx: newDirectNodeTx } = createNodeTxs(
23570
- output,
23571
- newNodeInput,
23572
- newDirectInput
23573
- );
23574
- const newCpfpNodeOutput = cpfpNodeTx.getOutput(0);
23575
- if (!newCpfpNodeOutput) {
23576
- throw Error("Could not get new cpfp node output");
23577
- }
23578
- const newDirectNodeOutput = newDirectNodeTx?.getOutput(0);
23579
- const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation({
23862
+ const keyDerivation = {
23580
23863
  type: "leaf" /* LEAF */,
23581
23864
  path: node.id
23582
- });
23865
+ };
23866
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
23867
+ const nodeTx = getTxFromRawTxBytes(node.nodeTx);
23868
+ const refundTx = getTxFromRawTxBytes(node.refundTx);
23869
+ const { nodeTx: newNodeTx, directNodeTx: newDirectNodeTx } = createDecrementedTimelockNodeTx(parentTx, nodeTx);
23583
23870
  signingJobs.push({
23584
23871
  signingPublicKey,
23585
- rawTx: cpfpNodeTx.toBytes(),
23872
+ rawTx: newNodeTx.toBytes(),
23586
23873
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
23587
23874
  type: "node",
23588
- parentTxOut: parentNodeOutput
23875
+ parentTxOut: unsignedParentNodeOutput,
23876
+ leafId: node.id,
23877
+ keyDerivation: {
23878
+ type: "leaf" /* LEAF */,
23879
+ path: node.id
23880
+ },
23881
+ verifyingKey: node.verifyingPublicKey
23589
23882
  });
23590
23883
  if (newDirectNodeTx) {
23591
23884
  signingJobs.push({
@@ -23593,537 +23886,299 @@ var TransferService = class extends BaseTransferService {
23593
23886
  rawTx: newDirectNodeTx.toBytes(),
23594
23887
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
23595
23888
  type: "directNode",
23596
- parentTxOut: parentNodeOutput
23889
+ parentTxOut: unsignedParentNodeOutput,
23890
+ leafId: node.id,
23891
+ keyDerivation: {
23892
+ type: "leaf" /* LEAF */,
23893
+ path: node.id
23894
+ },
23895
+ verifyingKey: node.verifyingPublicKey
23597
23896
  });
23598
23897
  }
23599
- const newCpfpRefundOutPoint = {
23600
- txid: (0, import_utils15.hexToBytes)(getTxId(cpfpNodeTx)),
23601
- index: 0
23602
- };
23603
- let newDirectRefundOutPoint;
23604
- if (newDirectNodeTx) {
23605
- newDirectRefundOutPoint = {
23606
- txid: (0, import_utils15.hexToBytes)(getTxId(newDirectNodeTx)),
23607
- index: 0
23608
- };
23898
+ const newCpfpNodeOutput = newNodeTx.getOutput(0);
23899
+ if (!newCpfpNodeOutput) {
23900
+ throw Error("Could not get new cpfp node output");
23609
23901
  }
23610
- const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
23611
- sequence: INITIAL_SEQUENCE,
23612
- directSequence: INITIAL_DIRECT_SEQUENCE,
23613
- input: newCpfpRefundOutPoint,
23614
- directInput: newDirectRefundOutPoint,
23615
- amountSats: nodeOutput.amount,
23616
- receivingPubkey: await this.config.signer.getPublicKeyFromDerivation({
23617
- type: "leaf" /* LEAF */,
23618
- path: node.id
23619
- }),
23902
+ const newDirectNodeOutput = newDirectNodeTx?.getOutput(0);
23903
+ const amountSats = refundTx.getOutput(0).amount;
23904
+ if (amountSats === void 0) {
23905
+ throw new Error("Amount not found in extendTimelock");
23906
+ }
23907
+ const directAmountSats = newDirectNodeOutput?.amount;
23908
+ if (directAmountSats === void 0) {
23909
+ throw new Error("Amount not found in extendTimelock");
23910
+ }
23911
+ const {
23912
+ cpfpRefundTx: newRefundTx,
23913
+ directRefundTx: newDirectRefundTx,
23914
+ directFromCpfpRefundTx: newDirectFromCpfpRefundTx
23915
+ } = createInitialTimelockRefundTxs({
23916
+ nodeTx: newNodeTx,
23917
+ directNodeTx: newDirectNodeTx,
23918
+ receivingPubkey: signingPublicKey,
23620
23919
  network: this.config.getNetwork()
23621
23920
  });
23622
23921
  signingJobs.push({
23623
23922
  signingPublicKey,
23624
- rawTx: cpfpRefundTx.toBytes(),
23923
+ rawTx: newRefundTx.toBytes(),
23625
23924
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
23626
23925
  type: "cpfp",
23627
- parentTxOut: newCpfpNodeOutput
23926
+ parentTxOut: newCpfpNodeOutput,
23927
+ leafId: node.id,
23928
+ keyDerivation,
23929
+ verifyingKey: node.verifyingPublicKey
23628
23930
  });
23629
- if (directRefundTx && newDirectNodeOutput) {
23931
+ if (newDirectRefundTx && newDirectNodeOutput) {
23630
23932
  signingJobs.push({
23631
23933
  signingPublicKey,
23632
- rawTx: directRefundTx.toBytes(),
23934
+ rawTx: newDirectRefundTx.toBytes(),
23633
23935
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
23634
23936
  type: "direct",
23635
- parentTxOut: newDirectNodeOutput
23937
+ parentTxOut: newDirectNodeOutput,
23938
+ leafId: node.id,
23939
+ keyDerivation,
23940
+ verifyingKey: node.verifyingPublicKey
23636
23941
  });
23637
23942
  }
23638
- if (directFromCpfpRefundTx && newCpfpNodeOutput) {
23943
+ if (newDirectFromCpfpRefundTx && newDirectNodeOutput) {
23639
23944
  signingJobs.push({
23640
23945
  signingPublicKey,
23641
- rawTx: directFromCpfpRefundTx.toBytes(),
23946
+ rawTx: newDirectFromCpfpRefundTx.toBytes(),
23642
23947
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
23643
23948
  type: "directFromCpfp",
23644
- parentTxOut: newCpfpNodeOutput
23949
+ parentTxOut: newCpfpNodeOutput,
23950
+ leafId: node.id,
23951
+ keyDerivation,
23952
+ verifyingKey: node.verifyingPublicKey
23645
23953
  });
23646
23954
  }
23647
- const sparkClient = await this.connectionManager.createSparkClient(
23648
- this.config.getCoordinatorAddress()
23649
- );
23650
- const response = await sparkClient.refresh_timelock_v2({
23651
- leafId: node.id,
23652
- ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
23653
- signingJobs: signingJobs.map(getSigningJobProto)
23654
- });
23655
- if (signingJobs.length !== response.signingResults.length) {
23656
- throw Error(
23657
- `number of signing jobs and signing results do not match: ${signingJobs.length} !== ${response.signingResults.length}`
23658
- );
23659
- }
23660
- let nodeSignatures = [];
23661
- let leafCpfpSignature;
23662
- let leafDirectSignature;
23663
- let cpfpRefundSignature;
23664
- let directRefundSignature;
23665
- let directFromCpfpRefundSignature;
23666
- for (const [i, signingResult] of response.signingResults.entries()) {
23667
- const signingJob = signingJobs[i];
23668
- if (!signingJob || !signingResult) {
23669
- throw Error("Signing job does not exist");
23670
- }
23671
- const rawTx = getTxFromRawTxBytes(signingJob.rawTx);
23672
- const txOut = signingJob.parentTxOut;
23673
- if (!txOut) {
23674
- throw Error("Could not get tx out");
23675
- }
23676
- const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
23677
- const userSignature = await this.config.signer.signFrost({
23678
- message: rawTxSighash,
23679
- keyDerivation: {
23680
- type: "leaf" /* LEAF */,
23681
- path: node.id
23682
- },
23683
- publicKey: signingJob.signingPublicKey,
23684
- verifyingKey: signingResult.verifyingKey,
23685
- selfCommitment: signingJob.signingNonceCommitment,
23686
- statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
23687
- adaptorPubKey: new Uint8Array()
23688
- });
23689
- const signature = await this.config.signer.aggregateFrost({
23690
- message: rawTxSighash,
23691
- statechainSignatures: signingResult.signingResult?.signatureShares,
23692
- statechainPublicKeys: signingResult.signingResult?.publicKeys,
23693
- verifyingKey: signingResult.verifyingKey,
23694
- statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
23695
- selfCommitment: signingJob.signingNonceCommitment,
23696
- publicKey: signingJob.signingPublicKey,
23697
- selfSignature: userSignature,
23698
- adaptorPubKey: new Uint8Array()
23699
- });
23700
- if (signingJob.type === "node") {
23701
- leafCpfpSignature = signature;
23702
- } else if (signingJob.type === "directNode") {
23703
- leafDirectSignature = signature;
23704
- } else if (signingJob.type === "cpfp") {
23705
- cpfpRefundSignature = signature;
23706
- } else if (signingJob.type === "direct") {
23707
- directRefundSignature = signature;
23708
- } else if (signingJob.type === "directFromCpfp") {
23709
- directFromCpfpRefundSignature = signature;
23710
- }
23711
- }
23712
- nodeSignatures.push({
23713
- nodeId: node.id,
23714
- nodeTxSignature: leafCpfpSignature || new Uint8Array(),
23715
- directNodeTxSignature: leafDirectSignature || new Uint8Array(),
23716
- refundTxSignature: cpfpRefundSignature || new Uint8Array(),
23717
- directRefundTxSignature: directRefundSignature || new Uint8Array(),
23718
- directFromCpfpRefundTxSignature: directFromCpfpRefundSignature || new Uint8Array()
23719
- });
23720
- const result = await sparkClient.finalize_node_signatures_v2({
23721
- intent: 3 /* REFRESH */,
23722
- nodeSignatures
23723
- });
23724
- return result;
23725
- }
23726
- async refreshTimelockNodes(node, parentNode) {
23727
- return await this.refreshTimelockNodesInternal(node, parentNode);
23955
+ return signingJobs;
23728
23956
  }
23729
- async extendTimelock(node) {
23730
- const nodeTx = getTxFromRawTxBytes(node.nodeTx);
23731
- const refundTx = getTxFromRawTxBytes(node.refundTx);
23732
- const refundSequence = refundTx.getInput(0).sequence || 0;
23733
- const newNodeOutPoint = {
23734
- txid: (0, import_utils15.hexToBytes)(getTxId(nodeTx)),
23735
- index: 0
23736
- };
23737
- const {
23738
- nextSequence: newNodeSequence,
23739
- nextDirectSequence: newDirectNodeSequence
23740
- } = getNextTransactionSequence(refundSequence);
23741
- const newNodeTx = new import_btc_signer2.Transaction({
23742
- version: 3,
23743
- allowUnknownOutputs: true
23744
- });
23745
- newNodeTx.addInput({ ...newNodeOutPoint, sequence: newNodeSequence });
23746
- const originalOutput = nodeTx.getOutput(0);
23747
- if (!originalOutput) {
23748
- throw Error("Could not get original node output");
23749
- }
23750
- newNodeTx.addOutput({
23751
- script: originalOutput.script,
23752
- amount: originalOutput.amount
23753
- });
23754
- newNodeTx.addOutput(getEphemeralAnchorOutput());
23755
- let newDirectNodeTx;
23756
- if (node.directTx.length > 0) {
23757
- newDirectNodeTx = new import_btc_signer2.Transaction({
23758
- version: 3,
23759
- allowUnknownOutputs: true
23760
- });
23761
- newDirectNodeTx.addInput({
23762
- ...newNodeOutPoint,
23763
- sequence: newDirectNodeSequence
23764
- });
23765
- newDirectNodeTx.addOutput({
23766
- script: originalOutput.script,
23767
- amount: maybeApplyFee(originalOutput.amount)
23768
- });
23769
- }
23770
- const newCpfpRefundOutPoint = {
23771
- txid: (0, import_utils15.hexToBytes)(getTxId(newNodeTx)),
23772
- index: 0
23773
- };
23774
- let newDirectRefundOutPoint;
23775
- if (newDirectNodeTx) {
23776
- newDirectRefundOutPoint = {
23777
- txid: (0, import_utils15.hexToBytes)(getTxId(newDirectNodeTx)),
23778
- index: 0
23779
- };
23780
- }
23781
- const amountSats = refundTx.getOutput(0).amount;
23782
- if (amountSats === void 0) {
23783
- throw new Error("Amount not found in extendTimelock");
23784
- }
23785
- const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation({
23786
- type: "leaf" /* LEAF */,
23787
- path: node.id
23788
- });
23789
- const {
23790
- cpfpRefundTx: newCpfpRefundTx,
23791
- directRefundTx: newDirectRefundTx,
23792
- directFromCpfpRefundTx: newDirectFromCpfpRefundTx
23793
- } = createRefundTxs({
23794
- sequence: INITIAL_SEQUENCE,
23795
- directSequence: INITIAL_DIRECT_SEQUENCE,
23796
- input: newCpfpRefundOutPoint,
23797
- directInput: newDirectRefundOutPoint,
23798
- amountSats,
23799
- receivingPubkey: signingPublicKey,
23800
- network: this.config.getNetwork()
23801
- });
23802
- if (!newCpfpRefundTx) {
23803
- throw new ValidationError(
23804
- "Failed to create refund transactions in extendTimelock"
23805
- );
23806
- }
23807
- const nodeSighash = getSigHashFromTx(newNodeTx, 0, nodeTx.getOutput(0));
23808
- const directNodeSighash = newDirectNodeTx ? getSigHashFromTx(newDirectNodeTx, 0, nodeTx.getOutput(0)) : void 0;
23809
- const cpfpRefundSighash = getSigHashFromTx(
23810
- newCpfpRefundTx,
23811
- 0,
23812
- newNodeTx.getOutput(0)
23957
+ async createRenewNodeSigningJobs(node, parentNode) {
23958
+ const signingJobs = [];
23959
+ const parentTx = getTxFromRawTxBytes(parentNode.nodeTx);
23960
+ const parentNodeOutput = getTxFromRawTxBytes(parentNode.nodeTx).getOutput(
23961
+ 0
23813
23962
  );
23814
- const directRefundSighash = newDirectNodeTx && newDirectRefundTx ? getSigHashFromTx(newDirectRefundTx, 0, newDirectNodeTx.getOutput(0)) : void 0;
23815
- const directFromCpfpRefundSighash = newDirectFromCpfpRefundTx ? getSigHashFromTx(newDirectFromCpfpRefundTx, 0, newNodeTx.getOutput(0)) : void 0;
23816
- const newNodeSigningJob = {
23817
- signingPublicKey,
23818
- rawTx: newNodeTx.toBytes(),
23819
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
23820
- };
23821
- const newDirectNodeSigningJob = newDirectNodeTx ? {
23822
- signingPublicKey,
23823
- rawTx: newDirectNodeTx.toBytes(),
23824
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
23825
- } : void 0;
23826
- const newCpfpRefundSigningJob = {
23827
- signingPublicKey,
23828
- rawTx: newCpfpRefundTx.toBytes(),
23829
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
23963
+ const unsignedParentNodeOutput = {
23964
+ script: parentNodeOutput.script,
23965
+ amount: parentNodeOutput.amount
23830
23966
  };
23831
- const newDirectRefundSigningJob = newDirectRefundTx ? {
23832
- signingPublicKey,
23833
- rawTx: newDirectRefundTx.toBytes(),
23834
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
23835
- } : void 0;
23836
- const newDirectFromCpfpRefundSigningJob = newDirectFromCpfpRefundTx ? {
23837
- signingPublicKey,
23838
- rawTx: newDirectFromCpfpRefundTx.toBytes(),
23839
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
23840
- } : void 0;
23841
- const sparkClient = await this.connectionManager.createSparkClient(
23842
- this.config.getCoordinatorAddress()
23843
- );
23844
- const response = await sparkClient.extend_leaf_v2({
23845
- leafId: node.id,
23846
- ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
23847
- nodeTxSigningJob: getSigningJobProto(newNodeSigningJob),
23848
- directNodeTxSigningJob: newDirectNodeSigningJob ? getSigningJobProto(newDirectNodeSigningJob) : void 0,
23849
- refundTxSigningJob: getSigningJobProto(newCpfpRefundSigningJob),
23850
- directRefundTxSigningJob: newDirectRefundSigningJob ? getSigningJobProto(newDirectRefundSigningJob) : void 0,
23851
- directFromCpfpRefundTxSigningJob: newDirectFromCpfpRefundSigningJob ? getSigningJobProto(newDirectFromCpfpRefundSigningJob) : void 0
23852
- });
23853
- if (!response.nodeTxSigningResult || !response.refundTxSigningResult) {
23854
- throw new Error("Signing result does not exist");
23855
- }
23856
23967
  const keyDerivation = {
23857
23968
  type: "leaf" /* LEAF */,
23858
23969
  path: node.id
23859
23970
  };
23860
- const nodeUserSig = await this.config.signer.signFrost({
23861
- message: nodeSighash,
23971
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
23972
+ const { nodeTx: splitNodeTx, directNodeTx: splitNodeDirectTx } = createZeroTimelockNodeTx(parentTx);
23973
+ signingJobs.push({
23974
+ signingPublicKey,
23975
+ rawTx: splitNodeTx.toBytes(),
23976
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
23977
+ type: "split",
23978
+ parentTxOut: unsignedParentNodeOutput,
23979
+ leafId: node.id,
23862
23980
  keyDerivation,
23863
- publicKey: signingPublicKey,
23864
- verifyingKey: response.nodeTxSigningResult.verifyingKey,
23865
- selfCommitment: newNodeSigningJob.signingNonceCommitment,
23866
- statechainCommitments: response.nodeTxSigningResult.signingResult?.signingNonceCommitments,
23867
- adaptorPubKey: new Uint8Array()
23981
+ verifyingKey: node.verifyingPublicKey
23868
23982
  });
23869
- const nodeSig = await this.config.signer.aggregateFrost({
23870
- message: nodeSighash,
23871
- statechainSignatures: response.nodeTxSigningResult.signingResult?.signatureShares,
23872
- statechainPublicKeys: response.nodeTxSigningResult.signingResult?.publicKeys,
23873
- verifyingKey: response.nodeTxSigningResult.verifyingKey,
23874
- statechainCommitments: response.nodeTxSigningResult.signingResult?.signingNonceCommitments,
23875
- selfCommitment: newNodeSigningJob.signingNonceCommitment,
23876
- publicKey: signingPublicKey,
23877
- selfSignature: nodeUserSig,
23878
- adaptorPubKey: new Uint8Array()
23879
- });
23880
- let directNodeSig;
23881
- if (directNodeSighash && newDirectNodeSigningJob && response.directNodeTxSigningResult) {
23882
- const directNodeUserSig = await this.config.signer.signFrost({
23883
- message: directNodeSighash,
23983
+ if (splitNodeDirectTx) {
23984
+ signingJobs.push({
23985
+ signingPublicKey,
23986
+ rawTx: splitNodeDirectTx.toBytes(),
23987
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
23988
+ type: "directSplit",
23989
+ parentTxOut: unsignedParentNodeOutput,
23990
+ leafId: node.id,
23884
23991
  keyDerivation,
23885
- publicKey: signingPublicKey,
23886
- verifyingKey: response.directNodeTxSigningResult.verifyingKey,
23887
- selfCommitment: newDirectNodeSigningJob.signingNonceCommitment,
23888
- statechainCommitments: response.directNodeTxSigningResult.signingResult?.signingNonceCommitments,
23889
- adaptorPubKey: new Uint8Array()
23890
- });
23891
- directNodeSig = await this.config.signer.aggregateFrost({
23892
- message: directNodeSighash,
23893
- statechainSignatures: response.directNodeTxSigningResult.signingResult?.signatureShares,
23894
- statechainPublicKeys: response.directNodeTxSigningResult.signingResult?.publicKeys,
23895
- verifyingKey: response.directNodeTxSigningResult.verifyingKey,
23896
- statechainCommitments: response.directNodeTxSigningResult.signingResult?.signingNonceCommitments,
23897
- selfCommitment: newDirectNodeSigningJob.signingNonceCommitment,
23898
- publicKey: signingPublicKey,
23899
- selfSignature: directNodeUserSig,
23900
- adaptorPubKey: new Uint8Array()
23992
+ verifyingKey: node.verifyingPublicKey
23901
23993
  });
23902
23994
  }
23903
- const cpfpRefundUserSig = await this.config.signer.signFrost({
23904
- message: cpfpRefundSighash,
23995
+ const splitNodeOutput = splitNodeTx.getOutput(0);
23996
+ const splitNodeDirectOutput = splitNodeDirectTx.getOutput(0);
23997
+ if (!splitNodeDirectOutput.amount || !splitNodeDirectOutput.script) {
23998
+ throw new Error("Could not get split node output");
23999
+ }
24000
+ const unsignedSplitNodeOutput = {
24001
+ script: splitNodeDirectOutput.script,
24002
+ amount: splitNodeDirectOutput.amount
24003
+ };
24004
+ const { nodeTx: newNodeTx, directNodeTx: newDirectNodeTx } = createInitialTimelockNodeTx(splitNodeTx);
24005
+ signingJobs.push({
24006
+ signingPublicKey,
24007
+ rawTx: newNodeTx.toBytes(),
24008
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
24009
+ type: "node",
24010
+ parentTxOut: splitNodeOutput,
24011
+ leafId: node.id,
23905
24012
  keyDerivation,
23906
- publicKey: signingPublicKey,
23907
- verifyingKey: response.refundTxSigningResult.verifyingKey,
23908
- selfCommitment: newCpfpRefundSigningJob.signingNonceCommitment,
23909
- statechainCommitments: response.refundTxSigningResult.signingResult?.signingNonceCommitments,
23910
- adaptorPubKey: new Uint8Array()
24013
+ verifyingKey: node.verifyingPublicKey
23911
24014
  });
23912
- const cpfpRefundSig = await this.config.signer.aggregateFrost({
23913
- message: cpfpRefundSighash,
23914
- statechainSignatures: response.refundTxSigningResult.signingResult?.signatureShares,
23915
- statechainPublicKeys: response.refundTxSigningResult.signingResult?.publicKeys,
23916
- verifyingKey: response.refundTxSigningResult.verifyingKey,
23917
- statechainCommitments: response.refundTxSigningResult.signingResult?.signingNonceCommitments,
23918
- selfCommitment: newCpfpRefundSigningJob.signingNonceCommitment,
23919
- publicKey: signingPublicKey,
23920
- selfSignature: cpfpRefundUserSig,
23921
- adaptorPubKey: new Uint8Array()
23922
- });
23923
- let directRefundSig;
23924
- if (directRefundSighash && newDirectRefundSigningJob && response.directRefundTxSigningResult) {
23925
- const directRefundUserSig = await this.config.signer.signFrost({
23926
- message: directRefundSighash,
23927
- keyDerivation,
23928
- publicKey: signingPublicKey,
23929
- verifyingKey: response.directRefundTxSigningResult.verifyingKey,
23930
- selfCommitment: newDirectRefundSigningJob.signingNonceCommitment,
23931
- statechainCommitments: response.directRefundTxSigningResult.signingResult?.signingNonceCommitments,
23932
- adaptorPubKey: new Uint8Array()
23933
- });
23934
- directRefundSig = await this.config.signer.aggregateFrost({
23935
- message: directRefundSighash,
23936
- statechainSignatures: response.directRefundTxSigningResult.signingResult?.signatureShares,
23937
- statechainPublicKeys: response.directRefundTxSigningResult.signingResult?.publicKeys,
23938
- verifyingKey: response.directRefundTxSigningResult.verifyingKey,
23939
- statechainCommitments: response.directRefundTxSigningResult.signingResult?.signingNonceCommitments,
23940
- selfCommitment: newDirectRefundSigningJob.signingNonceCommitment,
23941
- publicKey: signingPublicKey,
23942
- selfSignature: directRefundUserSig,
23943
- adaptorPubKey: new Uint8Array()
23944
- });
23945
- }
23946
- let directFromCpfpRefundSig;
23947
- if (directFromCpfpRefundSighash && newDirectFromCpfpRefundSigningJob && response.directFromCpfpRefundTxSigningResult) {
23948
- const directFromCpfpRefundUserSig = await this.config.signer.signFrost({
23949
- message: directFromCpfpRefundSighash,
24015
+ if (newDirectNodeTx) {
24016
+ signingJobs.push({
24017
+ signingPublicKey,
24018
+ rawTx: newDirectNodeTx.toBytes(),
24019
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
24020
+ type: "directNode",
24021
+ parentTxOut: splitNodeOutput,
24022
+ leafId: node.id,
23950
24023
  keyDerivation,
23951
- publicKey: signingPublicKey,
23952
- verifyingKey: response.directFromCpfpRefundTxSigningResult.verifyingKey,
23953
- selfCommitment: newDirectFromCpfpRefundSigningJob.signingNonceCommitment,
23954
- statechainCommitments: response.directFromCpfpRefundTxSigningResult.signingResult?.signingNonceCommitments,
23955
- adaptorPubKey: new Uint8Array()
23956
- });
23957
- directFromCpfpRefundSig = await this.config.signer.aggregateFrost({
23958
- message: directFromCpfpRefundSighash,
23959
- statechainSignatures: response.directFromCpfpRefundTxSigningResult.signingResult?.signatureShares,
23960
- statechainPublicKeys: response.directFromCpfpRefundTxSigningResult.signingResult?.publicKeys,
23961
- verifyingKey: response.directFromCpfpRefundTxSigningResult.verifyingKey,
23962
- statechainCommitments: response.directFromCpfpRefundTxSigningResult.signingResult?.signingNonceCommitments,
23963
- selfCommitment: newDirectFromCpfpRefundSigningJob.signingNonceCommitment,
23964
- publicKey: signingPublicKey,
23965
- selfSignature: directFromCpfpRefundUserSig,
23966
- adaptorPubKey: new Uint8Array()
24024
+ verifyingKey: node.verifyingPublicKey
23967
24025
  });
23968
24026
  }
23969
- return await sparkClient.finalize_node_signatures_v2({
23970
- intent: 4 /* EXTEND */,
23971
- nodeSignatures: [
23972
- {
23973
- nodeId: response.leafId,
23974
- nodeTxSignature: nodeSig,
23975
- directNodeTxSignature: directNodeSig,
23976
- refundTxSignature: cpfpRefundSig,
23977
- directRefundTxSignature: directRefundSig,
23978
- directFromCpfpRefundTxSignature: directFromCpfpRefundSig
23979
- }
23980
- ]
23981
- });
23982
- }
23983
- async testonly_expireTimeLockNodeTx(node, parentNode) {
23984
- return await this.refreshTimelockNodesInternal(node, parentNode, true);
23985
- }
23986
- async testonly_expireTimeLockRefundtx(node) {
23987
- const nodeTx = getTxFromRawTxBytes(node.nodeTx);
23988
- const directNodeTx = node.directTx.length > 0 ? getTxFromRawTxBytes(node.directTx) : void 0;
23989
- const cpfpRefundTx = getTxFromRawTxBytes(node.refundTx);
23990
- const currSequence = cpfpRefundTx.getInput(0).sequence || 0;
23991
- const currTimelock = getCurrentTimelock(currSequence);
23992
- if (currTimelock <= 100) {
23993
- throw new ValidationError("Cannot expire timelock below 100", {
23994
- field: "currTimelock",
23995
- value: currTimelock,
23996
- expected: "Timelock greater than 100"
23997
- });
23998
- }
23999
- const nextSequence = TEST_UNILATERAL_SEQUENCE;
24000
- const nextDirectSequence = TEST_UNILATERAL_SEQUENCE + DIRECT_TIMELOCK_OFFSET;
24001
- const nodeOutput = nodeTx.getOutput(0);
24002
- if (!nodeOutput) {
24003
- throw Error("Could not get node output");
24004
- }
24005
- const keyDerivation = {
24006
- type: "leaf" /* LEAF */,
24007
- path: node.id
24008
- };
24009
- const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
24010
- const cpfpRefundOutPoint = {
24011
- txid: (0, import_utils15.hexToBytes)(getTxId(nodeTx)),
24012
- index: 0
24013
- };
24014
- let directRefundOutPoint;
24015
- if (directNodeTx) {
24016
- directRefundOutPoint = {
24017
- txid: (0, import_utils15.hexToBytes)(getTxId(directNodeTx)),
24018
- index: 0
24019
- };
24027
+ const newCpfpNodeOutput = newNodeTx.getOutput(0);
24028
+ if (!newCpfpNodeOutput) {
24029
+ throw Error("Could not get new cpfp node output");
24020
24030
  }
24031
+ const newDirectNodeOutput = newDirectNodeTx?.getOutput(0);
24021
24032
  const {
24022
- cpfpRefundTx: newCpfpRefundTx,
24033
+ cpfpRefundTx: newRefundTx,
24023
24034
  directRefundTx: newDirectRefundTx,
24024
24035
  directFromCpfpRefundTx: newDirectFromCpfpRefundTx
24025
- } = createRefundTxs({
24026
- sequence: nextSequence,
24027
- directSequence: nextDirectSequence,
24028
- input: cpfpRefundOutPoint,
24029
- directInput: directRefundOutPoint,
24030
- amountSats: nodeOutput.amount,
24036
+ } = createInitialTimelockRefundTxs({
24037
+ nodeTx: newNodeTx,
24038
+ directNodeTx: newDirectNodeTx,
24031
24039
  receivingPubkey: signingPublicKey,
24032
24040
  network: this.config.getNetwork()
24033
24041
  });
24034
- const signingJobs = [];
24035
24042
  signingJobs.push({
24036
24043
  signingPublicKey,
24037
- rawTx: newCpfpRefundTx.toBytes(),
24044
+ rawTx: newRefundTx.toBytes(),
24038
24045
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
24039
24046
  type: "cpfp",
24040
- parentTxOut: nodeOutput
24047
+ parentTxOut: newCpfpNodeOutput,
24048
+ leafId: node.id,
24049
+ keyDerivation,
24050
+ verifyingKey: node.verifyingPublicKey
24041
24051
  });
24042
- const directNodeTxOut = directNodeTx?.getOutput(0);
24043
- if (newDirectRefundTx && directNodeTxOut) {
24052
+ if (newDirectRefundTx && newDirectNodeOutput) {
24044
24053
  signingJobs.push({
24045
24054
  signingPublicKey,
24046
24055
  rawTx: newDirectRefundTx.toBytes(),
24047
24056
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
24048
24057
  type: "direct",
24049
- parentTxOut: directNodeTxOut
24058
+ parentTxOut: newDirectNodeOutput,
24059
+ leafId: node.id,
24060
+ keyDerivation,
24061
+ verifyingKey: node.verifyingPublicKey
24050
24062
  });
24051
24063
  }
24052
- if (newDirectFromCpfpRefundTx) {
24064
+ if (newDirectFromCpfpRefundTx && newDirectNodeOutput) {
24053
24065
  signingJobs.push({
24054
24066
  signingPublicKey,
24055
24067
  rawTx: newDirectFromCpfpRefundTx.toBytes(),
24056
24068
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
24057
24069
  type: "directFromCpfp",
24058
- parentTxOut: nodeOutput
24070
+ parentTxOut: newCpfpNodeOutput,
24071
+ leafId: node.id,
24072
+ keyDerivation,
24073
+ verifyingKey: node.verifyingPublicKey
24059
24074
  });
24060
24075
  }
24076
+ return signingJobs;
24077
+ }
24078
+ async renewZeroTimelockNodeTxn(node) {
24061
24079
  const sparkClient = await this.connectionManager.createSparkClient(
24062
24080
  this.config.getCoordinatorAddress()
24063
24081
  );
24064
- const response = await sparkClient.refresh_timelock_v2({
24065
- leafId: node.id,
24066
- ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
24067
- signingJobs: signingJobs.map(getSigningJobProto)
24082
+ const signingJobs = await this.createRenewZeroTimelockNodeSigningJobs(node);
24083
+ const statechainCommitments = await sparkClient.get_signing_commitments({
24084
+ nodeIds: [node.id],
24085
+ count: signingJobs.length
24068
24086
  });
24069
- if (response.signingResults.length !== signingJobs.length) {
24070
- throw Error(
24071
- `Expected ${signingJobs.length} signing results, got ${response.signingResults.length}`
24072
- );
24073
- }
24074
- let cpfpRefundSignature;
24075
- let directRefundSignature;
24076
- let directFromCpfpRefundSignature;
24077
- for (const [i, signingJob] of signingJobs.entries()) {
24078
- const signingResult = response.signingResults[i];
24079
- if (!signingResult) {
24080
- throw Error("Signing result does not exist");
24087
+ const mappedSigningJobs = signingJobs.map((signingJob, index) => {
24088
+ const signingNonceCommitments = statechainCommitments.signingCommitments[index]?.signingNonceCommitments;
24089
+ if (!signingNonceCommitments) {
24090
+ throw new ValidationError("Signing nonce commitments not found", {
24091
+ field: "signingNonceCommitments",
24092
+ value: signingNonceCommitments
24093
+ });
24081
24094
  }
24082
- const rawTx = getTxFromRawTxBytes(signingJob.rawTx);
24083
- const txOut = signingJob.parentTxOut;
24084
- const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
24085
- const userSignature = await this.config.signer.signFrost({
24086
- message: rawTxSighash,
24087
- keyDerivation,
24088
- publicKey: signingPublicKey,
24089
- verifyingKey: signingResult.verifyingKey,
24090
- selfCommitment: signingJob.signingNonceCommitment,
24091
- statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
24092
- adaptorPubKey: new Uint8Array()
24093
- });
24094
- const signature = await this.config.signer.aggregateFrost({
24095
- message: rawTxSighash,
24096
- statechainSignatures: signingResult.signingResult?.signatureShares,
24097
- statechainPublicKeys: signingResult.signingResult?.publicKeys,
24098
- verifyingKey: signingResult.verifyingKey,
24099
- statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
24100
- selfCommitment: signingJob.signingNonceCommitment,
24101
- publicKey: signingPublicKey,
24102
- selfSignature: userSignature,
24103
- adaptorPubKey: new Uint8Array()
24104
- });
24105
- if (signingJob.type === "cpfp") {
24106
- cpfpRefundSignature = signature;
24107
- } else if (signingJob.type === "direct") {
24108
- directRefundSignature = signature;
24109
- } else if (signingJob.type === "directFromCpfp") {
24110
- directFromCpfpRefundSignature = signature;
24095
+ return {
24096
+ ...signingJob,
24097
+ signingNonceCommitments
24098
+ };
24099
+ });
24100
+ const userSignedTxSigningJobs = await this.signingService.signSigningJobs(mappedSigningJobs);
24101
+ const renewZeroTimelockNodeSigningJob = {
24102
+ nodeTxSigningJob: userSignedTxSigningJobs.get("node"),
24103
+ refundTxSigningJob: userSignedTxSigningJobs.get("cpfp"),
24104
+ directNodeTxSigningJob: userSignedTxSigningJobs.get("directNode"),
24105
+ directRefundTxSigningJob: void 0,
24106
+ directFromCpfpRefundTxSigningJob: userSignedTxSigningJobs.get("directFromCpfp")
24107
+ };
24108
+ const response = await sparkClient.renew_leaf({
24109
+ leafId: node.id,
24110
+ signingJobs: {
24111
+ $case: "renewNodeZeroTimelockSigningJob",
24112
+ renewNodeZeroTimelockSigningJob: renewZeroTimelockNodeSigningJob
24111
24113
  }
24114
+ });
24115
+ if (response.renewResult?.$case !== "renewNodeZeroTimelockResult" || !response.renewResult?.renewNodeZeroTimelockResult.node) {
24116
+ throw new ValidationError("Unexpected renew result", {
24117
+ field: "renewResult",
24118
+ value: response.renewResult
24119
+ });
24112
24120
  }
24113
- const result = await sparkClient.finalize_node_signatures_v2({
24114
- intent: 3 /* REFRESH */,
24115
- nodeSignatures: [
24116
- {
24117
- nodeId: node.id,
24118
- nodeTxSignature: new Uint8Array(),
24119
- directNodeTxSignature: new Uint8Array(),
24120
- refundTxSignature: cpfpRefundSignature,
24121
- directRefundTxSignature: directRefundSignature,
24122
- directFromCpfpRefundTxSignature: directFromCpfpRefundSignature
24123
- }
24124
- ]
24121
+ return response.renewResult.renewNodeZeroTimelockResult.node;
24122
+ }
24123
+ async createRenewZeroTimelockNodeSigningJobs(node) {
24124
+ const signingJobs = [];
24125
+ const keyDerivation = {
24126
+ type: "leaf" /* LEAF */,
24127
+ path: node.id
24128
+ };
24129
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
24130
+ const nodeTx = getTxFromRawTxBytes(node.nodeTx);
24131
+ const { nodeTx: newNodeTx, directNodeTx: newDirectNodeTx } = createZeroTimelockNodeTx(nodeTx);
24132
+ signingJobs.push({
24133
+ signingPublicKey,
24134
+ rawTx: newNodeTx.toBytes(),
24135
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
24136
+ type: "node",
24137
+ parentTxOut: nodeTx.getOutput(0),
24138
+ leafId: node.id,
24139
+ keyDerivation,
24140
+ verifyingKey: node.verifyingPublicKey
24125
24141
  });
24126
- return result;
24142
+ signingJobs.push({
24143
+ signingPublicKey,
24144
+ rawTx: newDirectNodeTx.toBytes(),
24145
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
24146
+ type: "directNode",
24147
+ parentTxOut: nodeTx.getOutput(0),
24148
+ leafId: node.id,
24149
+ keyDerivation,
24150
+ verifyingKey: node.verifyingPublicKey
24151
+ });
24152
+ const { cpfpRefundTx, directFromCpfpRefundTx } = createInitialTimelockRefundTxs({
24153
+ nodeTx: newNodeTx,
24154
+ directNodeTx: newDirectNodeTx,
24155
+ receivingPubkey: signingPublicKey,
24156
+ network: this.config.getNetwork()
24157
+ });
24158
+ signingJobs.push({
24159
+ signingPublicKey,
24160
+ rawTx: cpfpRefundTx.toBytes(),
24161
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
24162
+ type: "cpfp",
24163
+ parentTxOut: newNodeTx.getOutput(0),
24164
+ leafId: node.id,
24165
+ keyDerivation,
24166
+ verifyingKey: node.verifyingPublicKey
24167
+ });
24168
+ if (!directFromCpfpRefundTx) {
24169
+ throw new Error("Could not create direct refund transactions");
24170
+ }
24171
+ signingJobs.push({
24172
+ signingPublicKey,
24173
+ rawTx: directFromCpfpRefundTx.toBytes(),
24174
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
24175
+ type: "directFromCpfp",
24176
+ parentTxOut: newNodeTx.getOutput(0),
24177
+ leafId: node.id,
24178
+ keyDerivation,
24179
+ verifyingKey: node.verifyingPublicKey
24180
+ });
24181
+ return signingJobs;
24127
24182
  }
24128
24183
  };
24129
24184
 
@@ -24165,71 +24220,6 @@ var CoopExitService = class extends BaseTransferService {
24165
24220
  directFromCpfpSignaturesMap
24166
24221
  };
24167
24222
  }
24168
- createConnectorRefundTransactions(sequence, directSequence, cpfpNodeOutPoint, directNodeOutPoint, connectorOutput, amountSats, receiverPubKey) {
24169
- const cpfpRefundTx = new import_btc_signer3.Transaction();
24170
- if (!cpfpNodeOutPoint.txid || cpfpNodeOutPoint.index === void 0) {
24171
- throw new ValidationError("Invalid CPFP node outpoint", {
24172
- field: "cpfpNodeOutPoint",
24173
- value: { txid: cpfpNodeOutPoint.txid, index: cpfpNodeOutPoint.index },
24174
- expected: "Both txid and index must be defined"
24175
- });
24176
- }
24177
- cpfpRefundTx.addInput({
24178
- txid: cpfpNodeOutPoint.txid,
24179
- index: cpfpNodeOutPoint.index,
24180
- sequence
24181
- });
24182
- cpfpRefundTx.addInput(connectorOutput);
24183
- const receiverScript = getP2TRScriptFromPublicKey(
24184
- receiverPubKey,
24185
- this.config.getNetwork()
24186
- );
24187
- cpfpRefundTx.addOutput({
24188
- script: receiverScript,
24189
- amount: amountSats
24190
- });
24191
- let directRefundTx;
24192
- let directFromCpfpRefundTx;
24193
- if (directNodeOutPoint) {
24194
- if (!directNodeOutPoint.txid || directNodeOutPoint.index === void 0) {
24195
- throw new ValidationError("Invalid direct node outpoint", {
24196
- field: "directNodeOutPoint",
24197
- value: {
24198
- txid: directNodeOutPoint.txid,
24199
- index: directNodeOutPoint.index
24200
- },
24201
- expected: "Both txid and index must be defined"
24202
- });
24203
- }
24204
- directRefundTx = new import_btc_signer3.Transaction();
24205
- directRefundTx.addInput({
24206
- txid: directNodeOutPoint.txid,
24207
- index: directNodeOutPoint.index,
24208
- sequence: directSequence
24209
- });
24210
- directRefundTx.addInput(connectorOutput);
24211
- directRefundTx.addOutput({
24212
- script: receiverScript,
24213
- amount: maybeApplyFee(amountSats)
24214
- });
24215
- directFromCpfpRefundTx = new import_btc_signer3.Transaction();
24216
- directFromCpfpRefundTx.addInput({
24217
- txid: cpfpNodeOutPoint.txid,
24218
- index: cpfpNodeOutPoint.index,
24219
- sequence: directSequence
24220
- });
24221
- directFromCpfpRefundTx.addInput(connectorOutput);
24222
- directFromCpfpRefundTx.addOutput({
24223
- script: receiverScript,
24224
- amount: maybeApplyFee(amountSats)
24225
- });
24226
- }
24227
- return {
24228
- cpfpRefundTx,
24229
- directRefundTx,
24230
- directFromCpfpRefundTx
24231
- };
24232
- }
24233
24223
  async signCoopExitRefunds(leaves, exitTxId, connectorOutputs, receiverPubKey, transferId) {
24234
24224
  if (leaves.length !== connectorOutputs.length) {
24235
24225
  throw new ValidationError(
@@ -24263,29 +24253,39 @@ var CoopExitService = class extends BaseTransferService {
24263
24253
  expected: "Valid connector output"
24264
24254
  });
24265
24255
  }
24256
+ const nodeTx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
24257
+ let directNodeTx;
24258
+ if (leaf.leaf.directTx.length > 0) {
24259
+ directNodeTx = getTxFromRawTxBytes(leaf.leaf.directTx);
24260
+ }
24266
24261
  const currentRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
24267
- const sequence = currentRefundTx.getInput(0).sequence;
24268
- if (!sequence) {
24262
+ if (!currentRefundTx) {
24263
+ throw new ValidationError("Invalid refund transaction", {
24264
+ field: "currentRefundTx",
24265
+ value: currentRefundTx,
24266
+ expected: "Non-null refund transaction"
24267
+ });
24268
+ }
24269
+ const currentSequence = currentRefundTx.getInput(0).sequence;
24270
+ if (!currentSequence) {
24269
24271
  throw new ValidationError("Invalid refund transaction", {
24270
24272
  field: "sequence",
24271
24273
  value: currentRefundTx.getInput(0),
24272
24274
  expected: "Non-null sequence"
24273
24275
  });
24274
24276
  }
24275
- const { nextSequence, nextDirectSequence } = getNextTransactionSequence(sequence);
24276
24277
  let currentDirectRefundTx;
24277
24278
  if (leaf.leaf.directRefundTx.length > 0) {
24278
24279
  currentDirectRefundTx = getTxFromRawTxBytes(leaf.leaf.directRefundTx);
24279
24280
  }
24280
- const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = this.createConnectorRefundTransactions(
24281
- nextSequence,
24282
- nextDirectSequence,
24283
- currentRefundTx.getInput(0),
24284
- currentDirectRefundTx?.getInput(0),
24281
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createConnectorRefundTxs({
24282
+ nodeTx,
24283
+ directNodeTx,
24284
+ sequence: currentSequence,
24285
24285
  connectorOutput,
24286
- BigInt(leaf.leaf.value),
24287
- receiverPubKey
24288
- );
24286
+ receivingPubkey: receiverPubKey,
24287
+ network: this.config.getNetwork()
24288
+ });
24289
24289
  const signingNonceCommitment = await this.config.signer.getRandomSigningCommitment();
24290
24290
  const directSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
24291
24291
  const directFromCpfpSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
@@ -24392,9 +24392,9 @@ var CoopExitService = class extends BaseTransferService {
24392
24392
  init_buffer();
24393
24393
  var import_secp256k110 = require("@noble/curves/secp256k1");
24394
24394
  var import_sha29 = require("@noble/hashes/sha2");
24395
- var import_utils16 = require("@noble/hashes/utils");
24396
- var import_btc_signer4 = require("@scure/btc-signer");
24397
- var import_utils17 = require("@scure/btc-signer/utils");
24395
+ var import_utils17 = require("@noble/hashes/utils");
24396
+ var import_btc_signer2 = require("@scure/btc-signer");
24397
+ var import_utils18 = require("@scure/btc-signer/utils");
24398
24398
  var DepositService = class {
24399
24399
  config;
24400
24400
  connectionManager;
@@ -24422,7 +24422,7 @@ var DepositService = class {
24422
24422
  operatorPubkey,
24423
24423
  address.address
24424
24424
  );
24425
- const taprootKey = (0, import_btc_signer4.p2tr)(
24425
+ const taprootKey = (0, import_btc_signer2.p2tr)(
24426
24426
  operatorPubkey.slice(1, 33),
24427
24427
  void 0,
24428
24428
  getNetwork(this.config.getNetwork())
@@ -24446,7 +24446,7 @@ var DepositService = class {
24446
24446
  if (operator.identifier === this.config.getCoordinatorIdentifier() && !verifyCoordinatorProof) {
24447
24447
  continue;
24448
24448
  }
24449
- const operatorPubkey2 = (0, import_utils16.hexToBytes)(operator.identityPublicKey);
24449
+ const operatorPubkey2 = (0, import_utils17.hexToBytes)(operator.identityPublicKey);
24450
24450
  const operatorSig = address.depositAddressProof.addressSignatures[operator.identifier];
24451
24451
  if (!operatorSig) {
24452
24452
  throw new ValidationError("Operator signature not found", {
@@ -24565,38 +24565,18 @@ var DepositService = class {
24565
24565
  expected: "Valid output index"
24566
24566
  });
24567
24567
  }
24568
- const script = output.script;
24569
- const amount = output.amount;
24570
- if (!script || !amount) {
24571
- throw new ValidationError("No script or amount found in deposit tx", {
24572
- field: "output",
24573
- value: output,
24574
- expected: "Output with script and amount"
24575
- });
24576
- }
24577
- const depositOutPoint = {
24578
- txid: (0, import_utils16.hexToBytes)(getTxId(depositTx)),
24579
- index: vout
24580
- };
24581
- const depositTxOut = {
24582
- script,
24583
- amount
24584
- };
24585
- const [cpfpRootTx, directRootTx] = createRootTx(
24586
- depositOutPoint,
24587
- depositTxOut
24568
+ const { nodeTx: cpfpRootTx, directNodeTx: directRootTx } = createRootNodeTx(
24569
+ depositTx,
24570
+ vout
24588
24571
  );
24589
24572
  const cpfpRootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
24590
24573
  const directRootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
24591
24574
  const cpfpRootTxSighash = getSigHashFromTx(cpfpRootTx, 0, output);
24592
24575
  const directRootTxSighash = getSigHashFromTx(directRootTx, 0, output);
24593
24576
  const signingPubKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
24594
- const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
24595
- sequence: INITIAL_SEQUENCE,
24596
- directSequence: INITIAL_DIRECT_SEQUENCE,
24597
- input: { txid: (0, import_utils16.hexToBytes)(getTxId(cpfpRootTx)), index: 0 },
24598
- directInput: { txid: (0, import_utils16.hexToBytes)(getTxId(directRootTx)), index: 0 },
24599
- amountSats: amount,
24577
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createInitialTimelockRefundTxs({
24578
+ nodeTx: cpfpRootTx,
24579
+ directNodeTx: directRootTx,
24600
24580
  receivingPubkey: signingPubKey,
24601
24581
  network: this.config.getNetwork()
24602
24582
  });
@@ -24725,7 +24705,7 @@ var DepositService = class {
24725
24705
  }
24726
24706
  );
24727
24707
  }
24728
- if (!(0, import_utils17.equalBytes)(treeResp.rootNodeSignatureShares.verifyingKey, verifyingKey)) {
24708
+ if (!(0, import_utils18.equalBytes)(treeResp.rootNodeSignatureShares.verifyingKey, verifyingKey)) {
24729
24709
  throw new ValidationError("Verifying key mismatch", {
24730
24710
  field: "verifyingKey",
24731
24711
  value: treeResp.rootNodeSignatureShares.verifyingKey,
@@ -24888,22 +24868,12 @@ var DepositService = class {
24888
24868
  expected: "Output with script and amount"
24889
24869
  });
24890
24870
  }
24891
- const depositOutPoint = {
24892
- txid: (0, import_utils16.hexToBytes)(getTxId(depositTx)),
24893
- index: vout
24894
- };
24895
- const depositTxOut = {
24896
- script,
24897
- amount
24898
- };
24899
- const [cpfpRootTx, _] = createRootTx(depositOutPoint, depositTxOut);
24871
+ const { nodeTx: cpfpRootTx } = createRootNodeTx(depositTx, vout);
24900
24872
  const cpfpRootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
24901
24873
  const cpfpRootTxSighash = getSigHashFromTx(cpfpRootTx, 0, output);
24902
24874
  const signingPubKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
24903
- const { cpfpRefundTx } = createRefundTxs({
24904
- sequence: INITIAL_SEQUENCE,
24905
- input: { txid: (0, import_utils16.hexToBytes)(getTxId(cpfpRootTx)), index: 0 },
24906
- amountSats: amount,
24875
+ const { cpfpRefundTx } = createInitialTimelockRefundTxs({
24876
+ nodeTx: cpfpRootTx,
24907
24877
  receivingPubkey: signingPubKey,
24908
24878
  network: this.config.getNetwork()
24909
24879
  });
@@ -24972,7 +24942,7 @@ var DepositService = class {
24972
24942
  }
24973
24943
  );
24974
24944
  }
24975
- if (!(0, import_utils17.equalBytes)(treeResp.rootNodeSignatureShares.verifyingKey, verifyingKey)) {
24945
+ if (!(0, import_utils18.equalBytes)(treeResp.rootNodeSignatureShares.verifyingKey, verifyingKey)) {
24976
24946
  throw new ValidationError("Verifying key mismatch", {
24977
24947
  field: "verifyingKey",
24978
24948
  value: treeResp.rootNodeSignatureShares.verifyingKey,
@@ -25049,7 +25019,7 @@ var DepositService = class {
25049
25019
  // src/services/lightning.ts
25050
25020
  init_buffer();
25051
25021
  var import_secp256k111 = require("@noble/curves/secp256k1");
25052
- var import_utils18 = require("@noble/curves/utils");
25022
+ var import_utils19 = require("@noble/curves/utils");
25053
25023
  var import_sha210 = require("@noble/hashes/sha2");
25054
25024
  var import_uuidv74 = require("uuidv7");
25055
25025
 
@@ -25135,8 +25105,8 @@ var LightningService = class {
25135
25105
  }) {
25136
25106
  const crypto = getCrypto();
25137
25107
  const randBytes = crypto.getRandomValues(new Uint8Array(32));
25138
- const preimage = (0, import_utils18.numberToBytesBE)(
25139
- (0, import_utils18.bytesToNumberBE)(randBytes) % import_secp256k111.secp256k1.CURVE.n,
25108
+ const preimage = (0, import_utils19.numberToBytesBE)(
25109
+ (0, import_utils19.bytesToNumberBE)(randBytes) % import_secp256k111.secp256k1.CURVE.n,
25140
25110
  32
25141
25111
  );
25142
25112
  return await this.createLightningInvoiceWithPreImage({
@@ -25191,12 +25161,12 @@ var LightningService = class {
25191
25161
  const sparkClient = await this.connectionManager.createSparkClient(
25192
25162
  operator.address
25193
25163
  );
25194
- const userIdentityPublicKey = receiverIdentityPubkey ? (0, import_utils18.hexToBytes)(receiverIdentityPubkey) : await this.config.signer.getIdentityPublicKey();
25164
+ const userIdentityPublicKey = receiverIdentityPubkey ? (0, import_utils19.hexToBytes)(receiverIdentityPubkey) : await this.config.signer.getIdentityPublicKey();
25195
25165
  try {
25196
25166
  await sparkClient.store_preimage_share({
25197
25167
  paymentHash,
25198
25168
  preimageShare: {
25199
- secretShare: (0, import_utils18.numberToBytesBE)(share.share, 32),
25169
+ secretShare: (0, import_utils19.numberToBytesBE)(share.share, 32),
25200
25170
  proofs: share.proofs
25201
25171
  },
25202
25172
  threshold: this.config.getThreshold(),
@@ -25414,8 +25384,8 @@ var LightningService = class {
25414
25384
 
25415
25385
  // src/services/token-transactions.ts
25416
25386
  init_buffer();
25417
- var import_utils19 = require("@noble/curves/utils");
25418
- var import_utils20 = require("@noble/hashes/utils");
25387
+ var import_utils20 = require("@noble/curves/utils");
25388
+ var import_utils21 = require("@noble/hashes/utils");
25419
25389
 
25420
25390
  // src/utils/token-hashing.ts
25421
25391
  init_buffer();
@@ -29560,14 +29530,14 @@ var TokenTransactionService = class {
29560
29530
  }
29561
29531
  if (receiverAddress.sparkInvoiceFields) {
29562
29532
  return {
29563
- receiverPublicKey: (0, import_utils20.hexToBytes)(receiverAddress.identityPublicKey),
29533
+ receiverPublicKey: (0, import_utils21.hexToBytes)(receiverAddress.identityPublicKey),
29564
29534
  rawTokenIdentifier,
29565
29535
  tokenAmount: transfer.tokenAmount,
29566
29536
  sparkInvoice: transfer.receiverSparkAddress
29567
29537
  };
29568
29538
  }
29569
29539
  return {
29570
- receiverPublicKey: (0, import_utils20.hexToBytes)(receiverAddress.identityPublicKey),
29540
+ receiverPublicKey: (0, import_utils21.hexToBytes)(receiverAddress.identityPublicKey),
29571
29541
  rawTokenIdentifier,
29572
29542
  tokenAmount: transfer.tokenAmount
29573
29543
  };
@@ -29597,7 +29567,7 @@ var TokenTransactionService = class {
29597
29567
  (output) => ({
29598
29568
  ownerPublicKey: output.receiverPublicKey,
29599
29569
  tokenIdentifier: output.rawTokenIdentifier,
29600
- tokenAmount: (0, import_utils19.numberToBytesBE)(output.tokenAmount, 16)
29570
+ tokenAmount: (0, import_utils20.numberToBytesBE)(output.tokenAmount, 16)
29601
29571
  })
29602
29572
  );
29603
29573
  if (availableTokenAmount > totalRequestedAmount) {
@@ -29606,7 +29576,7 @@ var TokenTransactionService = class {
29606
29576
  tokenOutputs.push({
29607
29577
  ownerPublicKey: await this.config.signer.getIdentityPublicKey(),
29608
29578
  tokenIdentifier: firstTokenIdentifierBytes,
29609
- tokenAmount: (0, import_utils19.numberToBytesBE)(changeAmount, 16)
29579
+ tokenAmount: (0, import_utils20.numberToBytesBE)(changeAmount, 16)
29610
29580
  });
29611
29581
  }
29612
29582
  return {
@@ -29633,7 +29603,7 @@ var TokenTransactionService = class {
29633
29603
  for (const [_, operator] of Object.entries(
29634
29604
  this.config.getSigningOperators()
29635
29605
  )) {
29636
- operatorKeys.push((0, import_utils20.hexToBytes)(operator.identityPublicKey));
29606
+ operatorKeys.push((0, import_utils21.hexToBytes)(operator.identityPublicKey));
29637
29607
  }
29638
29608
  return operatorKeys;
29639
29609
  }
@@ -29650,7 +29620,7 @@ var TokenTransactionService = class {
29650
29620
  finalTokenTransactionHash,
29651
29621
  signingOperators
29652
29622
  );
29653
- return (0, import_utils19.bytesToHex)(finalTokenTransactionHash);
29623
+ return (0, import_utils20.bytesToHex)(finalTokenTransactionHash);
29654
29624
  }
29655
29625
  async startTokenTransaction(tokenTransaction, signingOperators, outputsToSpendSigningPublicKeys, outputsToSpendCommitments) {
29656
29626
  const sparkClient = await this.connectionManager.createSparkTokenClient(
@@ -29825,10 +29795,10 @@ var TokenTransactionService = class {
29825
29795
  });
29826
29796
  }
29827
29797
  for (const ownerPublicKey of ownerPublicKeys) {
29828
- isValidPublicKey((0, import_utils19.bytesToHex)(ownerPublicKey));
29798
+ isValidPublicKey((0, import_utils20.bytesToHex)(ownerPublicKey));
29829
29799
  }
29830
29800
  for (const issuerPublicKey of issuerPublicKeys) {
29831
- isValidPublicKey((0, import_utils19.bytesToHex)(issuerPublicKey));
29801
+ isValidPublicKey((0, import_utils20.bytesToHex)(issuerPublicKey));
29832
29802
  }
29833
29803
  for (const tokenIdentifier of tokenIdentifiers) {
29834
29804
  if (tokenIdentifier.length !== 32) {
@@ -29896,8 +29866,8 @@ var TokenTransactionService = class {
29896
29866
  this.config.getCoordinatorAddress()
29897
29867
  );
29898
29868
  let queryParams = {
29899
- issuerPublicKeys: issuerPublicKeys?.map(import_utils20.hexToBytes),
29900
- ownerPublicKeys: ownerPublicKeys?.map(import_utils20.hexToBytes),
29869
+ issuerPublicKeys: issuerPublicKeys?.map(import_utils21.hexToBytes),
29870
+ ownerPublicKeys: ownerPublicKeys?.map(import_utils21.hexToBytes),
29901
29871
  tokenIdentifiers: tokenIdentifiers?.map((identifier) => {
29902
29872
  const { tokenIdentifier } = decodeBech32mTokenIdentifier(
29903
29873
  identifier,
@@ -29905,7 +29875,7 @@ var TokenTransactionService = class {
29905
29875
  );
29906
29876
  return tokenIdentifier;
29907
29877
  }),
29908
- tokenTransactionHashes: tokenTransactionHashes?.map(import_utils20.hexToBytes),
29878
+ tokenTransactionHashes: tokenTransactionHashes?.map(import_utils21.hexToBytes),
29909
29879
  outputIds: outputIds || [],
29910
29880
  limit: pageSize,
29911
29881
  offset
@@ -29940,7 +29910,7 @@ var TokenTransactionService = class {
29940
29910
  });
29941
29911
  }
29942
29912
  const exactMatch = tokenOutputs.find(
29943
- (item) => (0, import_utils19.bytesToNumberBE)(item.output.tokenAmount) === tokenAmount
29913
+ (item) => (0, import_utils20.bytesToNumberBE)(item.output.tokenAmount) === tokenAmount
29944
29914
  );
29945
29915
  if (exactMatch) {
29946
29916
  return [exactMatch];
@@ -29951,7 +29921,7 @@ var TokenTransactionService = class {
29951
29921
  for (const outputWithPreviousTransactionData of tokenOutputs) {
29952
29922
  if (remainingAmount <= 0n) break;
29953
29923
  selectedOutputs.push(outputWithPreviousTransactionData);
29954
- remainingAmount -= (0, import_utils19.bytesToNumberBE)(
29924
+ remainingAmount -= (0, import_utils20.bytesToNumberBE)(
29955
29925
  outputWithPreviousTransactionData.output.tokenAmount
29956
29926
  );
29957
29927
  }
@@ -29966,14 +29936,14 @@ var TokenTransactionService = class {
29966
29936
  sortTokenOutputsByStrategy(tokenOutputs, strategy) {
29967
29937
  if (strategy === "SMALL_FIRST") {
29968
29938
  tokenOutputs.sort((a, b) => {
29969
- const amountA = (0, import_utils19.bytesToNumberBE)(a.output.tokenAmount);
29970
- const amountB = (0, import_utils19.bytesToNumberBE)(b.output.tokenAmount);
29939
+ const amountA = (0, import_utils20.bytesToNumberBE)(a.output.tokenAmount);
29940
+ const amountB = (0, import_utils20.bytesToNumberBE)(b.output.tokenAmount);
29971
29941
  return amountA < amountB ? -1 : amountA > amountB ? 1 : 0;
29972
29942
  });
29973
29943
  } else {
29974
29944
  tokenOutputs.sort((a, b) => {
29975
- const amountA = (0, import_utils19.bytesToNumberBE)(a.output.tokenAmount);
29976
- const amountB = (0, import_utils19.bytesToNumberBE)(b.output.tokenAmount);
29945
+ const amountA = (0, import_utils20.bytesToNumberBE)(a.output.tokenAmount);
29946
+ const amountB = (0, import_utils20.bytesToNumberBE)(b.output.tokenAmount);
29977
29947
  return amountB < amountA ? -1 : amountB > amountA ? 1 : 0;
29978
29948
  });
29979
29949
  }
@@ -29981,7 +29951,7 @@ var TokenTransactionService = class {
29981
29951
  // Helper function for deciding if the signer public key is the identity public key
29982
29952
  async signMessageWithKey(message, publicKey) {
29983
29953
  const tokenSignatures = this.config.getTokenSignatures();
29984
- if ((0, import_utils19.bytesToHex)(publicKey) === (0, import_utils19.bytesToHex)(await this.config.signer.getIdentityPublicKey())) {
29954
+ if ((0, import_utils20.bytesToHex)(publicKey) === (0, import_utils20.bytesToHex)(await this.config.signer.getIdentityPublicKey())) {
29985
29955
  if (tokenSignatures === "SCHNORR") {
29986
29956
  return await this.config.signer.signSchnorrWithIdentityKey(message);
29987
29957
  } else {
@@ -29990,8 +29960,8 @@ var TokenTransactionService = class {
29990
29960
  } else {
29991
29961
  throw new ValidationError("Invalid public key", {
29992
29962
  field: "publicKey",
29993
- value: (0, import_utils19.bytesToHex)(publicKey),
29994
- expected: (0, import_utils19.bytesToHex)(await this.config.signer.getIdentityPublicKey())
29963
+ value: (0, import_utils20.bytesToHex)(publicKey),
29964
+ expected: (0, import_utils20.bytesToHex)(await this.config.signer.getIdentityPublicKey())
29995
29965
  });
29996
29966
  }
29997
29967
  }
@@ -30010,7 +29980,7 @@ var TokenTransactionService = class {
30010
29980
  }
30011
29981
  const payload = {
30012
29982
  finalTokenTransactionHash,
30013
- operatorIdentityPublicKey: (0, import_utils20.hexToBytes)(operator.identityPublicKey)
29983
+ operatorIdentityPublicKey: (0, import_utils21.hexToBytes)(operator.identityPublicKey)
30014
29984
  };
30015
29985
  const payloadHash = await hashOperatorSpecificTokenTransactionSignablePayload(payload);
30016
29986
  const ownerSignature = await this.signMessageWithKey(
@@ -30032,7 +30002,7 @@ var TokenTransactionService = class {
30032
30002
  }
30033
30003
  const payload = {
30034
30004
  finalTokenTransactionHash,
30035
- operatorIdentityPublicKey: (0, import_utils20.hexToBytes)(operator.identityPublicKey)
30005
+ operatorIdentityPublicKey: (0, import_utils21.hexToBytes)(operator.identityPublicKey)
30036
30006
  };
30037
30007
  const payloadHash = await hashOperatorSpecificTokenTransactionSignablePayload(payload);
30038
30008
  const ownerSignature = await this.signMessageWithKey(
@@ -30048,7 +30018,7 @@ var TokenTransactionService = class {
30048
30018
  for (let i = 0; i < transferInput.outputsToSpend.length; i++) {
30049
30019
  const payload = {
30050
30020
  finalTokenTransactionHash,
30051
- operatorIdentityPublicKey: (0, import_utils20.hexToBytes)(operator.identityPublicKey)
30021
+ operatorIdentityPublicKey: (0, import_utils21.hexToBytes)(operator.identityPublicKey)
30052
30022
  };
30053
30023
  const payloadHash = await hashOperatorSpecificTokenTransactionSignablePayload(payload);
30054
30024
  let ownerSignature;
@@ -30065,7 +30035,7 @@ var TokenTransactionService = class {
30065
30035
  }
30066
30036
  inputTtxoSignaturesPerOperator.push({
30067
30037
  ttxoSignatures,
30068
- operatorIdentityPublicKey: (0, import_utils20.hexToBytes)(operator.identityPublicKey)
30038
+ operatorIdentityPublicKey: (0, import_utils21.hexToBytes)(operator.identityPublicKey)
30069
30039
  });
30070
30040
  }
30071
30041
  return inputTtxoSignaturesPerOperator;
@@ -30081,13 +30051,12 @@ var import_nice_grpc_common = require("nice-grpc-common");
30081
30051
 
30082
30052
  // src/services/signing.ts
30083
30053
  init_buffer();
30084
- var import_utils22 = require("@noble/curves/utils");
30085
30054
 
30086
30055
  // src/utils/htlc-transactions.ts
30087
30056
  init_buffer();
30088
- var import_btc_signer5 = require("@scure/btc-signer");
30057
+ var import_btc_signer3 = require("@scure/btc-signer");
30089
30058
  var import_secp256k112 = require("@noble/curves/secp256k1");
30090
- var import_utils21 = require("@noble/curves/utils");
30059
+ var import_utils22 = require("@noble/curves/utils");
30091
30060
  var PUB_KEY_BYTES = "0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0";
30092
30061
  function numsPoint() {
30093
30062
  const withdrawalPubKeyPoint = import_secp256k112.secp256k1.Point.fromHex(PUB_KEY_BYTES);
@@ -30165,10 +30134,10 @@ function createLightningHTLCTransaction({
30165
30134
  outAmount = maybeApplyFee(outAmount);
30166
30135
  }
30167
30136
  const input = {
30168
- txid: (0, import_utils21.hexToBytes)(getTxId(nodeTx)),
30137
+ txid: (0, import_utils22.hexToBytes)(getTxId(nodeTx)),
30169
30138
  index: 0
30170
30139
  };
30171
- const htlcTransaction = new import_btc_signer5.Transaction({
30140
+ const htlcTransaction = new import_btc_signer3.Transaction({
30172
30141
  version: 3,
30173
30142
  allowUnknownOutputs: true
30174
30143
  });
@@ -30207,12 +30176,12 @@ function createHTLCTaprootAddress({
30207
30176
  );
30208
30177
  const hashLockLeaf = { leafVersion: 192, script: hashLockScript };
30209
30178
  const sequenceLockLeaf = { leafVersion: 192, script: sequenceLockScript };
30210
- const scriptTree = (0, import_btc_signer5.taprootListToTree)([hashLockLeaf, sequenceLockLeaf]);
30211
- const p2trScript = (0, import_btc_signer5.p2tr)(numsKey, scriptTree, network, true).script;
30179
+ const scriptTree = (0, import_btc_signer3.taprootListToTree)([hashLockLeaf, sequenceLockLeaf]);
30180
+ const p2trScript = (0, import_btc_signer3.p2tr)(numsKey, scriptTree, network, true).script;
30212
30181
  return p2trScript;
30213
30182
  }
30214
30183
  function createHashLockScript(hash, pubkey) {
30215
- const result = import_btc_signer5.Script.encode([
30184
+ const result = import_btc_signer3.Script.encode([
30216
30185
  "SHA256",
30217
30186
  hash,
30218
30187
  "EQUALVERIFY",
@@ -30222,8 +30191,8 @@ function createHashLockScript(hash, pubkey) {
30222
30191
  return result;
30223
30192
  }
30224
30193
  function createSequenceLockScript(sequence, sequenceLockDestinationPubkey) {
30225
- const result = import_btc_signer5.Script.encode([
30226
- (0, import_btc_signer5.ScriptNum)().encode(BigInt(sequence)),
30194
+ const result = import_btc_signer3.Script.encode([
30195
+ (0, import_btc_signer3.ScriptNum)().encode(BigInt(sequence)),
30227
30196
  "CHECKSEQUENCEVERIFY",
30228
30197
  "DROP",
30229
30198
  sequenceLockDestinationPubkey.slice(1, 33),
@@ -30287,20 +30256,7 @@ var SigningService = class {
30287
30256
  });
30288
30257
  }
30289
30258
  const nodeTx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
30290
- const cpfpNodeOutPoint = {
30291
- txid: (0, import_utils22.hexToBytes)(getTxId(nodeTx)),
30292
- index: 0
30293
- };
30294
30259
  const currRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
30295
- const sequence = currRefundTx.getInput(0).sequence;
30296
- if (!sequence) {
30297
- throw new ValidationError("Invalid refund transaction", {
30298
- field: "sequence",
30299
- value: currRefundTx.getInput(0),
30300
- expected: "Non-null sequence"
30301
- });
30302
- }
30303
- const { nextSequence, nextDirectSequence } = getNextTransactionSequence(sequence);
30304
30260
  const amountSats = currRefundTx.getOutput(0).amount;
30305
30261
  if (amountSats === void 0) {
30306
30262
  throw new ValidationError("Invalid refund transaction", {
@@ -30310,20 +30266,21 @@ var SigningService = class {
30310
30266
  });
30311
30267
  }
30312
30268
  let directNodeTx;
30313
- let directNodeOutPoint;
30314
30269
  if (leaf.leaf.directTx.length > 0) {
30315
30270
  directNodeTx = getTxFromRawTxBytes(leaf.leaf.directTx);
30316
- directNodeOutPoint = {
30317
- txid: (0, import_utils22.hexToBytes)(getTxId(directNodeTx)),
30318
- index: 0
30319
- };
30320
30271
  }
30321
- const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
30322
- sequence: nextSequence,
30323
- directSequence: nextDirectSequence,
30324
- input: cpfpNodeOutPoint,
30325
- directInput: directNodeOutPoint,
30326
- amountSats,
30272
+ const currentSequence = currRefundTx.getInput(0).sequence;
30273
+ if (!currentSequence) {
30274
+ throw new ValidationError("Invalid refund transaction", {
30275
+ field: "sequence",
30276
+ value: currRefundTx.getInput(0),
30277
+ expected: "Non-null sequence"
30278
+ });
30279
+ }
30280
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createDecrementedTimelockRefundTxs({
30281
+ nodeTx,
30282
+ directNodeTx,
30283
+ sequence: currentSequence,
30327
30284
  receivingPubkey: receiverIdentityPubkey,
30328
30285
  network: this.config.getNetwork()
30329
30286
  });
@@ -30339,7 +30296,8 @@ var SigningService = class {
30339
30296
  cpfpSigningCommitments[i]?.signingNonceCommitments
30340
30297
  );
30341
30298
  cpfpLeafSigningJobs.push(...signingJobs);
30342
- if (directRefundTx) {
30299
+ const isZeroNode = getCurrentTimelock(nodeTx.getInput(0).sequence);
30300
+ if (directRefundTx && !isZeroNode) {
30343
30301
  if (!directNodeTx) {
30344
30302
  throw new ValidationError(
30345
30303
  "Direct node transaction undefined while direct refund transaction is defined",
@@ -30364,16 +30322,6 @@ var SigningService = class {
30364
30322
  directLeafSigningJobs.push(...signingJobs2);
30365
30323
  }
30366
30324
  if (directFromCpfpRefundTx) {
30367
- if (!directNodeTx) {
30368
- throw new ValidationError(
30369
- "Direct node transaction undefined while direct from CPFP refund transaction is defined",
30370
- {
30371
- field: "directNodeTx",
30372
- value: directNodeTx,
30373
- expected: "Non-null direct node transaction"
30374
- }
30375
- );
30376
- }
30377
30325
  const refundSighash2 = getSigHashFromTx(
30378
30326
  directFromCpfpRefundTx,
30379
30327
  0,
@@ -30500,6 +30448,35 @@ var SigningService = class {
30500
30448
  directFromCpfpLeafSigningJobs
30501
30449
  };
30502
30450
  }
30451
+ async signSigningJobs(signingJobs) {
30452
+ const userSignedTxSigningJobs = /* @__PURE__ */ new Map();
30453
+ for (const signingJob of signingJobs) {
30454
+ const rawTx = getTxFromRawTxBytes(signingJob.rawTx);
30455
+ const txOut = signingJob.parentTxOut;
30456
+ const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
30457
+ const userSignature = await this.config.signer.signFrost({
30458
+ message: rawTxSighash,
30459
+ keyDerivation: signingJob.keyDerivation,
30460
+ publicKey: signingJob.signingPublicKey,
30461
+ verifyingKey: signingJob.verifyingKey,
30462
+ selfCommitment: signingJob.signingNonceCommitment,
30463
+ statechainCommitments: signingJob.signingNonceCommitments,
30464
+ adaptorPubKey: new Uint8Array()
30465
+ });
30466
+ const userSignedTxSigningJob = {
30467
+ leafId: signingJob.leafId,
30468
+ signingPublicKey: signingJob.signingPublicKey,
30469
+ rawTx: rawTx.toBytes(),
30470
+ signingNonceCommitment: signingJob.signingNonceCommitment.commitment,
30471
+ signingCommitments: {
30472
+ signingCommitments: signingJob.signingNonceCommitments
30473
+ },
30474
+ userSignature
30475
+ };
30476
+ userSignedTxSigningJobs.set(signingJob.type, userSignedTxSigningJob);
30477
+ }
30478
+ return userSignedTxSigningJobs;
30479
+ }
30503
30480
  };
30504
30481
 
30505
30482
  // src/tests/utils/test-faucet.ts
@@ -30507,7 +30484,7 @@ init_buffer();
30507
30484
  var import_secp256k113 = require("@noble/curves/secp256k1");
30508
30485
  var import_utils23 = require("@noble/curves/utils");
30509
30486
  var btc5 = __toESM(require("@scure/btc-signer"), 1);
30510
- var import_btc_signer6 = require("@scure/btc-signer");
30487
+ var import_btc_signer4 = require("@scure/btc-signer");
30511
30488
  var import_utils24 = require("@scure/btc-signer/utils");
30512
30489
  var STATIC_FAUCET_KEY = (0, import_utils23.hexToBytes)(
30513
30490
  "deadbeef1337cafe4242424242424242deadbeef1337cafe4242424242424242"
@@ -30595,7 +30572,7 @@ var BitcoinFaucet = class _BitcoinFaucet {
30595
30572
  );
30596
30573
  await this.generateToAddress(1, address);
30597
30574
  const fundingTxRaw = await this.getRawTransaction(fundingTxid);
30598
- const fundingTx = import_btc_signer6.Transaction.fromRaw((0, import_utils23.hexToBytes)(fundingTxRaw.hex));
30575
+ const fundingTx = import_btc_signer4.Transaction.fromRaw((0, import_utils23.hexToBytes)(fundingTxRaw.hex));
30599
30576
  for (let i = 0; i < fundingTx.outputsLength; i++) {
30600
30577
  const output = fundingTx.getOutput(i);
30601
30578
  if (!output.script || !output.amount) continue;
@@ -30626,7 +30603,7 @@ var BitcoinFaucet = class _BitcoinFaucet {
30626
30603
  `Selected UTXO (${selectedUtxoAmountSats} sats) is too small to create even one faucet coin of ${COIN_AMOUNT} sats`
30627
30604
  );
30628
30605
  }
30629
- const splitTx = new import_btc_signer6.Transaction();
30606
+ const splitTx = new import_btc_signer4.Transaction();
30630
30607
  splitTx.addInput({
30631
30608
  txid: selectedUtxo.txid,
30632
30609
  index: selectedUtxo.vout
@@ -30669,7 +30646,7 @@ var BitcoinFaucet = class _BitcoinFaucet {
30669
30646
  }
30670
30647
  }
30671
30648
  async sendFaucetCoinToP2WPKHAddress(pubKey) {
30672
- const sendToPubKeyTx = new import_btc_signer6.Transaction();
30649
+ const sendToPubKeyTx = new import_btc_signer4.Transaction();
30673
30650
  const p2wpkhAddress = btc5.p2wpkh(pubKey, getNetwork(4 /* LOCAL */)).address;
30674
30651
  if (!p2wpkhAddress) {
30675
30652
  throw new Error("Invalid P2WPKH address");
@@ -30705,7 +30682,7 @@ var BitcoinFaucet = class _BitcoinFaucet {
30705
30682
  const sighash = unsignedTx.preimageWitnessV1(
30706
30683
  0,
30707
30684
  new Array(unsignedTx.inputsLength).fill(script),
30708
- import_btc_signer6.SigHash.DEFAULT,
30685
+ import_btc_signer4.SigHash.DEFAULT,
30709
30686
  new Array(unsignedTx.inputsLength).fill(fundingTxOut.amount)
30710
30687
  );
30711
30688
  const merkleRoot = new Uint8Array();
@@ -30724,6 +30701,14 @@ var BitcoinFaucet = class _BitcoinFaucet {
30724
30701
  async mineBlocks(numBlocks) {
30725
30702
  return await this.generateToAddress(numBlocks, this.miningAddress);
30726
30703
  }
30704
+ async mineBlocksAndWaitForMiningToComplete(numBlocks) {
30705
+ const startBlock = await this.getBlockCount();
30706
+ await this.mineBlocks(numBlocks);
30707
+ await this.waitForBlocksMined({
30708
+ startBlock,
30709
+ expectedIncrease: numBlocks
30710
+ });
30711
+ }
30727
30712
  async call(method, params) {
30728
30713
  try {
30729
30714
  const { fetch, Headers: Headers2 } = getFetch();
@@ -30776,27 +30761,62 @@ var BitcoinFaucet = class _BitcoinFaucet {
30776
30761
  async getBlock(blockHash) {
30777
30762
  return await this.call("getblock", [blockHash, 2]);
30778
30763
  }
30764
+ async getBlockCount() {
30765
+ return await this.call("getblockcount", []);
30766
+ }
30767
+ async waitForBlocksMined({
30768
+ startBlock,
30769
+ expectedIncrease,
30770
+ timeoutMs = 3e4,
30771
+ intervalMs = 5e3
30772
+ }) {
30773
+ const deadline = Date.now() + timeoutMs;
30774
+ await new Promise((r) => setTimeout(r, intervalMs));
30775
+ const start = startBlock;
30776
+ const target = start + expectedIncrease;
30777
+ while (Date.now() < deadline) {
30778
+ const currentBlock = await this.getBlockCount();
30779
+ if (currentBlock >= target) return currentBlock;
30780
+ await new Promise((r) => setTimeout(r, intervalMs));
30781
+ }
30782
+ throw new Error(
30783
+ `Timed out waiting for ${expectedIncrease} blocks (target height ${target})`
30784
+ );
30785
+ }
30779
30786
  async broadcastTx(txHex) {
30780
30787
  let response = await this.call("sendrawtransaction", [txHex, 0]);
30781
30788
  return response;
30782
30789
  }
30790
+ async submitPackage(txHexs) {
30791
+ let response = await this.call("submitpackage", [txHexs]);
30792
+ return response;
30793
+ }
30783
30794
  async getNewAddress() {
30784
30795
  const key = import_secp256k113.secp256k1.utils.randomPrivateKey();
30785
30796
  const pubKey = import_secp256k113.secp256k1.getPublicKey(key);
30786
30797
  return getP2TRAddressFromPublicKey(pubKey, 4 /* LOCAL */);
30787
30798
  }
30799
+ async getNewExternalWallet() {
30800
+ const key = import_secp256k113.secp256k1.utils.randomPrivateKey();
30801
+ const pubKey = import_secp256k113.secp256k1.getPublicKey(key);
30802
+ return {
30803
+ address: getP2TRAddressFromPublicKey(pubKey, 4 /* LOCAL */),
30804
+ key,
30805
+ pubKey
30806
+ };
30807
+ }
30788
30808
  async sendToAddress(address, amount, blocksToGenerate = 1) {
30789
30809
  const coin = await this.fund();
30790
30810
  if (!coin) {
30791
30811
  throw new Error("No coins available");
30792
30812
  }
30793
- const tx = new import_btc_signer6.Transaction();
30813
+ const tx = new import_btc_signer4.Transaction();
30794
30814
  tx.addInput(coin.outpoint);
30795
30815
  const availableAmount = COIN_AMOUNT - FEE_AMOUNT;
30796
- const destinationAddress = (0, import_btc_signer6.Address)(getNetwork(4 /* LOCAL */)).decode(
30816
+ const destinationAddress = (0, import_btc_signer4.Address)(getNetwork(4 /* LOCAL */)).decode(
30797
30817
  address
30798
30818
  );
30799
- const destinationScript = import_btc_signer6.OutScript.encode(destinationAddress);
30819
+ const destinationScript = import_btc_signer4.OutScript.encode(destinationAddress);
30800
30820
  tx.addOutput({
30801
30821
  script: destinationScript,
30802
30822
  amount
@@ -30841,76 +30861,6 @@ function chunkArray(arr, size) {
30841
30861
  return chunks;
30842
30862
  }
30843
30863
 
30844
- // src/utils/optimize.ts
30845
- init_buffer();
30846
- var DENOMINATIONS = Array.from({ length: 28 }, (_, i) => 2 ** i);
30847
- function assert(condition, message) {
30848
- if (!condition) {
30849
- throw new InternalValidationError(message || "Assertion failed");
30850
- }
30851
- }
30852
- function sum(arr) {
30853
- return arr.reduce((a, b) => a + b, 0);
30854
- }
30855
- function sorted(arr) {
30856
- return [...arr].sort((a, b) => a - b);
30857
- }
30858
- function equals(a, b) {
30859
- return a.length === b.length && a.every((val, index) => val === b[index]);
30860
- }
30861
- function greedyLeaves(amount) {
30862
- const leaves = [];
30863
- let remaining = amount;
30864
- for (let i = DENOMINATIONS.length - 1; i >= 0; i--) {
30865
- const leaf = DENOMINATIONS[i];
30866
- if (typeof leaf === "number" && leaf > 0) {
30867
- while (remaining >= leaf) {
30868
- remaining -= leaf;
30869
- leaves.push(leaf);
30870
- }
30871
- }
30872
- }
30873
- assert(sum(leaves) === amount, "greedy_leaves: sum mismatch");
30874
- return sorted(leaves);
30875
- }
30876
- var Swap = class {
30877
- inLeaves;
30878
- outLeaves;
30879
- constructor(inLeaves, outLeaves) {
30880
- this.inLeaves = [...inLeaves];
30881
- this.outLeaves = [...outLeaves];
30882
- assert(
30883
- sum(this.inLeaves) === sum(this.outLeaves),
30884
- "Swap in/out leaves must sum to same value for swap: " + this.toString()
30885
- );
30886
- }
30887
- toString() {
30888
- return `Swap(in=${JSON.stringify(this.inLeaves)}, out=${JSON.stringify(this.outLeaves)})`;
30889
- }
30890
- };
30891
- function maximizeUnilateralExit(inputLeaves, maxLeavesPerSwap = 64) {
30892
- const swaps = [];
30893
- let batch = [];
30894
- let leaves = sorted(inputLeaves);
30895
- while (leaves.length > 0) {
30896
- batch.push(leaves.shift());
30897
- const target = greedyLeaves(sum(batch));
30898
- if (batch.length >= maxLeavesPerSwap || target.length >= maxLeavesPerSwap) {
30899
- if (!equals(target, batch)) {
30900
- swaps.push(new Swap([...batch], target));
30901
- }
30902
- batch = [];
30903
- }
30904
- }
30905
- if (batch.length > 0) {
30906
- const target = greedyLeaves(sum(batch));
30907
- if (!equals(target, batch)) {
30908
- swaps.push(new Swap([...batch], target));
30909
- }
30910
- }
30911
- return swaps;
30912
- }
30913
-
30914
30864
  // src/utils/retry.ts
30915
30865
  init_buffer();
30916
30866
  var DEFAULT_RETRY_CONFIG = {
@@ -30989,6 +30939,200 @@ var SparkWalletEvent = {
30989
30939
  StreamReconnecting: "stream:reconnecting"
30990
30940
  };
30991
30941
 
30942
+ // src/utils/optimize.ts
30943
+ init_buffer();
30944
+ var DENOMINATIONS = Array.from({ length: 28 }, (_, i) => 2 ** i);
30945
+ function assert(condition, message) {
30946
+ if (!condition) {
30947
+ throw new InternalValidationError(message || "Assertion failed");
30948
+ }
30949
+ }
30950
+ function sum(arr) {
30951
+ return arr.reduce((a, b) => a + b, 0);
30952
+ }
30953
+ function sorted(arr) {
30954
+ return [...arr].sort((a, b) => a - b);
30955
+ }
30956
+ function equals(a, b) {
30957
+ return a.length === b.length && a.every((val, index) => val === b[index]);
30958
+ }
30959
+ function countOccurrences(arr) {
30960
+ const map = /* @__PURE__ */ new Map();
30961
+ for (const x of arr) {
30962
+ map.set(x, (map.get(x) ?? 0) + 1);
30963
+ }
30964
+ return map;
30965
+ }
30966
+ function subtractCounters(a, b) {
30967
+ const result = /* @__PURE__ */ new Map();
30968
+ for (const [key, value] of a.entries()) {
30969
+ const diff = value - (b.get(key) ?? 0);
30970
+ if (diff > 0) {
30971
+ result.set(key, diff);
30972
+ }
30973
+ }
30974
+ return result;
30975
+ }
30976
+ function counterToFlatArray(counter) {
30977
+ const arr = [];
30978
+ for (const [k, v] of Array.from(counter.entries()).sort(
30979
+ (a, b) => a[0] - b[0]
30980
+ )) {
30981
+ for (let i = 0; i < v; i++) {
30982
+ arr.push(k);
30983
+ }
30984
+ }
30985
+ return arr;
30986
+ }
30987
+ function greedyLeaves(amount) {
30988
+ const leaves = [];
30989
+ let remaining = amount;
30990
+ for (let i = DENOMINATIONS.length - 1; i >= 0; i--) {
30991
+ const leaf = DENOMINATIONS[i];
30992
+ if (typeof leaf === "number" && leaf > 0) {
30993
+ while (remaining >= leaf) {
30994
+ remaining -= leaf;
30995
+ leaves.push(leaf);
30996
+ }
30997
+ }
30998
+ }
30999
+ assert(sum(leaves) === amount, "greedy_leaves: sum mismatch");
31000
+ return sorted(leaves);
31001
+ }
31002
+ function swapMinimizingLeaves(amount, multiplicity = 1) {
31003
+ const leaves = [];
31004
+ let remaining = amount;
31005
+ assert(multiplicity > 0, "multiplicity must be > 0");
31006
+ for (const leaf of DENOMINATIONS) {
31007
+ if (typeof leaf === "number" && leaf > 0) {
31008
+ for (let i = 0; i < multiplicity; i++) {
31009
+ if (remaining >= leaf) {
31010
+ remaining -= leaf;
31011
+ leaves.push(leaf);
31012
+ }
31013
+ }
31014
+ }
31015
+ }
31016
+ leaves.push(...greedyLeaves(remaining));
31017
+ assert(sum(leaves) === amount, "swap_minimizing_leaves: sum mismatch");
31018
+ return sorted(leaves);
31019
+ }
31020
+ var Swap = class {
31021
+ inLeaves;
31022
+ outLeaves;
31023
+ constructor(inLeaves, outLeaves) {
31024
+ this.inLeaves = [...inLeaves];
31025
+ this.outLeaves = [...outLeaves];
31026
+ assert(
31027
+ sum(this.inLeaves) === sum(this.outLeaves),
31028
+ "Swap in/out leaves must sum to same value for swap: " + this.toString()
31029
+ );
31030
+ }
31031
+ toString() {
31032
+ return `Swap(in=${JSON.stringify(this.inLeaves)}, out=${JSON.stringify(this.outLeaves)})`;
31033
+ }
31034
+ };
31035
+ function maximizeUnilateralExit(inputLeaves, maxLeavesPerSwap = 64) {
31036
+ const swaps = [];
31037
+ let batch = [];
31038
+ let leaves = sorted(inputLeaves);
31039
+ while (leaves.length > 0) {
31040
+ batch.push(leaves.shift());
31041
+ const target = greedyLeaves(sum(batch));
31042
+ if (batch.length >= maxLeavesPerSwap || target.length >= maxLeavesPerSwap) {
31043
+ if (!equals(target, batch)) {
31044
+ swaps.push(new Swap([...batch], target));
31045
+ }
31046
+ batch = [];
31047
+ }
31048
+ }
31049
+ if (batch.length > 0) {
31050
+ const target = greedyLeaves(sum(batch));
31051
+ if (!equals(target, batch)) {
31052
+ swaps.push(new Swap([...batch], target));
31053
+ }
31054
+ }
31055
+ return swaps;
31056
+ }
31057
+ function minimizeTransferSwap(inputLeaves, multiplicity = 1, maxLeavesPerSwap = 64) {
31058
+ const balance = sum(inputLeaves);
31059
+ const optimalLeaves = swapMinimizingLeaves(balance, multiplicity);
31060
+ const walletCounter = countOccurrences(inputLeaves);
31061
+ const optimalCounter = countOccurrences(optimalLeaves);
31062
+ const leavesToGive = subtractCounters(walletCounter, optimalCounter);
31063
+ const leavesToReceive = subtractCounters(optimalCounter, walletCounter);
31064
+ const leavesToGiveFlat = counterToFlatArray(leavesToGive);
31065
+ const leavesToReceiveFlat = counterToFlatArray(leavesToReceive);
31066
+ const swaps = [];
31067
+ let toGiveBatch = [];
31068
+ let toReceiveBatch = [];
31069
+ let give = [...leavesToGiveFlat];
31070
+ let receive = [...leavesToReceiveFlat];
31071
+ while (give.length > 0 || receive.length > 0) {
31072
+ if (sum(toGiveBatch) > sum(toReceiveBatch)) {
31073
+ if (receive.length === 0) break;
31074
+ toReceiveBatch.push(receive.shift());
31075
+ } else {
31076
+ if (give.length === 0) break;
31077
+ toGiveBatch.push(give.shift());
31078
+ }
31079
+ if (toGiveBatch.length > 0 && toReceiveBatch.length > 0 && sum(toGiveBatch) === sum(toReceiveBatch)) {
31080
+ if (toGiveBatch.length > maxLeavesPerSwap) {
31081
+ for (let i = 0; i < toGiveBatch.length; i += maxLeavesPerSwap) {
31082
+ const subset = toGiveBatch.slice(i, i + maxLeavesPerSwap);
31083
+ swaps.push(new Swap(subset, greedyLeaves(sum(subset))));
31084
+ }
31085
+ } else if (toReceiveBatch.length > maxLeavesPerSwap) {
31086
+ for (let cutoff = maxLeavesPerSwap; cutoff > 0; cutoff--) {
31087
+ const sumCut = sum(toReceiveBatch.slice(0, cutoff));
31088
+ const remainder = sum(toGiveBatch) - sumCut;
31089
+ const alternateBatch = [
31090
+ ...toReceiveBatch.slice(0, cutoff),
31091
+ ...greedyLeaves(remainder)
31092
+ ];
31093
+ if (alternateBatch.length <= maxLeavesPerSwap) {
31094
+ swaps.push(new Swap([...toGiveBatch], alternateBatch));
31095
+ break;
31096
+ }
31097
+ }
31098
+ } else {
31099
+ swaps.push(new Swap([...toGiveBatch], [...toReceiveBatch]));
31100
+ }
31101
+ toGiveBatch = [];
31102
+ toReceiveBatch = [];
31103
+ }
31104
+ }
31105
+ return swaps;
31106
+ }
31107
+ function shouldOptimize(inputLeaves, multiplicity = 1, maxLeavesPerSwap = 64) {
31108
+ if (multiplicity == 0) {
31109
+ const swaps = maximizeUnilateralExit(inputLeaves, maxLeavesPerSwap);
31110
+ const numInputs = sum(swaps.map((swap) => swap.inLeaves.length));
31111
+ const numOutputs = sum(swaps.map((swap) => swap.outLeaves.length));
31112
+ return numOutputs * 5 < numInputs;
31113
+ } else {
31114
+ const swaps = minimizeTransferSwap(
31115
+ inputLeaves,
31116
+ multiplicity,
31117
+ maxLeavesPerSwap
31118
+ );
31119
+ const inputCounter = countOccurrences(
31120
+ swaps.flatMap((swap) => swap.inLeaves)
31121
+ );
31122
+ const outputCounter = countOccurrences(
31123
+ swaps.flatMap((swap) => swap.outLeaves)
31124
+ );
31125
+ return Math.abs(inputCounter.size - outputCounter.size) > 1;
31126
+ }
31127
+ }
31128
+ function optimize(inputLeaves, multiplicity = 1, maxLeavesPerSwap = 64) {
31129
+ if (multiplicity == 0) {
31130
+ return maximizeUnilateralExit(inputLeaves, maxLeavesPerSwap);
31131
+ } else {
31132
+ return minimizeTransferSwap(inputLeaves, multiplicity, maxLeavesPerSwap);
31133
+ }
31134
+ }
31135
+
30992
31136
  // src/spark-wallet/spark-wallet.ts
30993
31137
  var SparkWallet = class extends import_eventemitter3.EventEmitter {
30994
31138
  config;
@@ -31093,16 +31237,13 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
31093
31237
  if (event.transfer.transfer && !(0, import_utils25.equalBytes)(senderIdentityPublicKey, receiverIdentityPublicKey)) {
31094
31238
  await this.claimTransfer({
31095
31239
  transfer: event.transfer.transfer,
31096
- emit: true,
31097
- optimize: true
31240
+ emit: true
31098
31241
  });
31099
31242
  }
31100
31243
  } else if (isDepositStreamEvent(event)) {
31101
31244
  const deposit = event.deposit.deposit;
31102
- const newLeaf = await this.transferService.extendTimelock(deposit);
31103
- await this.transferLeavesToSelf(newLeaf.nodes, {
31104
- type: "leaf" /* LEAF */,
31105
- path: deposit.id
31245
+ await this.withLeaves(async () => {
31246
+ this.leaves.push(deposit);
31106
31247
  });
31107
31248
  this.emit(
31108
31249
  SparkWalletEvent.DepositConfirmed,
@@ -31278,19 +31419,6 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
31278
31419
  }
31279
31420
  return availableLeaves.filter(([_, node]) => !leavesToIgnore.has(node.id)).map(([_, node]) => node);
31280
31421
  }
31281
- async checkExtendLeaves(leaves) {
31282
- await this.withLeaves(async () => {
31283
- for (const leaf of leaves) {
31284
- if (!leaf.parentNodeId && leaf.status === "AVAILABLE") {
31285
- const res = await this.transferService.extendTimelock(leaf);
31286
- await this.transferLeavesToSelf(res.nodes, {
31287
- type: "leaf" /* LEAF */,
31288
- path: leaf.id
31289
- });
31290
- }
31291
- }
31292
- });
31293
- }
31294
31422
  verifyKey(pubkey1, pubkey2, verifyingKey) {
31295
31423
  return (0, import_utils25.equalBytes)(addPublicKeys(pubkey1, pubkey2), verifyingKey);
31296
31424
  }
@@ -31400,75 +31528,84 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
31400
31528
  }
31401
31529
  return nodes;
31402
31530
  }
31403
- areLeavesInefficient() {
31404
- const totalAmount = this.getInternalBalance();
31405
- if (this.leaves.length <= 1) {
31406
- return false;
31407
- }
31408
- const nextLowerPowerOfTwo = 31 - Math.clz32(totalAmount);
31409
- let remainingAmount = totalAmount;
31410
- let optimalLeavesLength = 0;
31411
- for (let i = nextLowerPowerOfTwo; i >= 0; i--) {
31412
- const denomination = 2 ** i;
31413
- while (remainingAmount >= denomination) {
31414
- remainingAmount -= denomination;
31415
- optimalLeavesLength++;
31416
- }
31531
+ async *optimizeLeaves(multiplicity = void 0) {
31532
+ const multiplicityValue = multiplicity ?? this.config.getOptimizationOptions().multiplicity ?? 0;
31533
+ if (multiplicityValue < 0) {
31534
+ throw new ValidationError("Multiplicity cannot be negative");
31535
+ } else if (multiplicityValue > 5) {
31536
+ throw new ValidationError("Multiplicity cannot be greater than 5");
31417
31537
  }
31418
- return this.leaves.length > optimalLeavesLength * 5;
31419
- }
31420
- async optimizeLeaves() {
31421
- if (this.optimizationInProgress || !this.areLeavesInefficient()) {
31538
+ if (this.optimizationInProgress || !shouldOptimize(
31539
+ this.leaves.map((leaf) => leaf.value),
31540
+ multiplicityValue
31541
+ )) {
31422
31542
  return;
31423
31543
  }
31424
- await this.withLeaves(async () => {
31544
+ const controller = new AbortController();
31545
+ const release = await this.leavesMutex.acquire();
31546
+ try {
31425
31547
  this.optimizationInProgress = true;
31426
- try {
31427
- this.leaves = await this.getLeaves();
31428
- const swaps = maximizeUnilateralExit(
31429
- this.leaves.map((leaf) => leaf.value)
31430
- );
31431
- const valueToNodes = /* @__PURE__ */ new Map();
31432
- this.leaves.forEach((leaf) => {
31433
- if (!valueToNodes.has(leaf.value)) {
31434
- valueToNodes.set(leaf.value, []);
31435
- }
31436
- valueToNodes.get(leaf.value).push(leaf);
31437
- });
31438
- for (const swap of swaps) {
31439
- const leavesToSend = [];
31440
- for (const leafValue of swap.inLeaves) {
31441
- const nodes = valueToNodes.get(leafValue);
31442
- if (nodes && nodes.length > 0) {
31443
- const node = nodes.shift();
31444
- leavesToSend.push(node);
31445
- } else {
31446
- throw new InternalValidationError(
31447
- `No unused leaf with value ${leafValue} found in leaves`
31448
- );
31449
- }
31548
+ this.leaves = await this.getLeaves();
31549
+ const swaps = optimize(
31550
+ this.leaves.map((leaf) => leaf.value),
31551
+ multiplicityValue
31552
+ );
31553
+ if (swaps.length === 0) {
31554
+ return;
31555
+ }
31556
+ yield {
31557
+ step: 0,
31558
+ total: swaps.length,
31559
+ controller
31560
+ };
31561
+ const valueToNodes = /* @__PURE__ */ new Map();
31562
+ this.leaves.forEach((leaf) => {
31563
+ if (!valueToNodes.has(leaf.value)) {
31564
+ valueToNodes.set(leaf.value, []);
31565
+ }
31566
+ valueToNodes.get(leaf.value).push(leaf);
31567
+ });
31568
+ for (const swap of swaps) {
31569
+ if (controller.signal.aborted) {
31570
+ break;
31571
+ }
31572
+ const leavesToSend = [];
31573
+ for (const leafValue of swap.inLeaves) {
31574
+ const nodes = valueToNodes.get(leafValue);
31575
+ if (nodes && nodes.length > 0) {
31576
+ const node = nodes.shift();
31577
+ leavesToSend.push(node);
31578
+ } else {
31579
+ throw new InternalValidationError(
31580
+ `No unused leaf with value ${leafValue} found in leaves`
31581
+ );
31450
31582
  }
31451
- await this.requestLeavesSwap({
31452
- leaves: leavesToSend,
31453
- targetAmounts: swap.outLeaves
31454
- });
31455
31583
  }
31456
- this.leaves = await this.getLeaves();
31457
- } finally {
31458
- this.optimizationInProgress = false;
31584
+ await this.requestLeavesSwap({
31585
+ leaves: leavesToSend,
31586
+ targetAmounts: swap.outLeaves
31587
+ });
31588
+ yield {
31589
+ step: swaps.indexOf(swap) + 1,
31590
+ total: swaps.length,
31591
+ controller
31592
+ };
31459
31593
  }
31460
- });
31594
+ this.leaves = await this.getLeaves();
31595
+ } finally {
31596
+ this.optimizationInProgress = false;
31597
+ release();
31598
+ }
31461
31599
  }
31462
31600
  async syncWallet() {
31463
31601
  await this.syncTokenOutputs();
31464
31602
  let leaves = await this.getLeaves();
31465
- leaves = await this.checkRefreshTimelockNodes(leaves);
31466
- leaves = await this.checkExtendTimeLockNodes(leaves);
31603
+ leaves = await this.checkRenewLeaves(leaves);
31467
31604
  this.leaves = leaves;
31468
- this.checkExtendLeaves(leaves);
31469
- this.optimizeLeaves().catch((e) => {
31470
- console.error("Failed to optimize leaves", e);
31471
- });
31605
+ if (this.config.getOptimizationOptions().auto) {
31606
+ for await (const _ of this.optimizeLeaves()) {
31607
+ }
31608
+ }
31472
31609
  }
31473
31610
  async withLeaves(operation) {
31474
31611
  const release = await this.leavesMutex.acquire();
@@ -32051,8 +32188,7 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
32051
32188
  }
32052
32189
  return await this.claimTransfer({
32053
32190
  transfer: incomingTransfer,
32054
- emit: false,
32055
- optimize: false
32191
+ emit: false
32056
32192
  });
32057
32193
  } catch (e) {
32058
32194
  console.error("[processSwapBatch] Error details:", {
@@ -32446,16 +32582,16 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
32446
32582
  }
32447
32583
  );
32448
32584
  }
32449
- const tx = new import_btc_signer7.Transaction();
32585
+ const tx = new import_btc_signer5.Transaction();
32450
32586
  tx.addInput({
32451
32587
  txid: depositTransactionId,
32452
32588
  index: outputIndex,
32453
32589
  witnessScript: new Uint8Array()
32454
32590
  });
32455
- const addressDecoded = (0, import_btc_signer7.Address)(getNetwork(network)).decode(
32591
+ const addressDecoded = (0, import_btc_signer5.Address)(getNetwork(network)).decode(
32456
32592
  destinationAddress
32457
32593
  );
32458
- const outputScript = import_btc_signer7.OutScript.encode(addressDecoded);
32594
+ const outputScript = import_btc_signer5.OutScript.encode(addressDecoded);
32459
32595
  tx.addOutput({
32460
32596
  script: outputScript,
32461
32597
  amount: BigInt(creditAmountSats)
@@ -32639,8 +32775,8 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
32639
32775
  if (!output) {
32640
32776
  continue;
32641
32777
  }
32642
- const parsedScript = import_btc_signer7.OutScript.decode(output.script);
32643
- const address = (0, import_btc_signer7.Address)(getNetwork(this.config.getNetwork())).encode(
32778
+ const parsedScript = import_btc_signer5.OutScript.decode(output.script);
32779
+ const address = (0, import_btc_signer5.Address)(getNetwork(this.config.getNetwork())).encode(
32644
32780
  parsedScript
32645
32781
  );
32646
32782
  if (staticDepositAddresses.has(address)) {
@@ -32717,26 +32853,7 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
32717
32853
  depositTx,
32718
32854
  vout
32719
32855
  });
32720
- const resultingNodes = [];
32721
- for (const node of res.nodes) {
32722
- if (node.status === "AVAILABLE") {
32723
- const { nodes } = await this.transferService.extendTimelock(node);
32724
- for (const n of nodes) {
32725
- if (n.status === "AVAILABLE") {
32726
- const transfer = await this.transferLeavesToSelf([n], {
32727
- type: "leaf" /* LEAF */,
32728
- path: node.id
32729
- });
32730
- resultingNodes.push(...transfer);
32731
- } else {
32732
- resultingNodes.push(n);
32733
- }
32734
- }
32735
- } else {
32736
- resultingNodes.push(node);
32737
- }
32738
- }
32739
- return resultingNodes;
32856
+ return res.nodes;
32740
32857
  }
32741
32858
  /**
32742
32859
  * Gets all unused deposit addresses for the wallet.
@@ -32845,8 +32962,8 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
32845
32962
  if (!output) {
32846
32963
  continue;
32847
32964
  }
32848
- const parsedScript = import_btc_signer7.OutScript.decode(output.script);
32849
- const address = (0, import_btc_signer7.Address)(getNetwork(this.config.getNetwork())).encode(
32965
+ const parsedScript = import_btc_signer5.OutScript.decode(output.script);
32966
+ const address = (0, import_btc_signer5.Address)(getNetwork(this.config.getNetwork())).encode(
32850
32967
  parsedScript
32851
32968
  );
32852
32969
  if (unusedDepositAddresses.has(address)) {
@@ -32878,6 +32995,9 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
32878
32995
  depositTx,
32879
32996
  vout
32880
32997
  });
32998
+ await this.withLeaves(async () => {
32999
+ this.leaves.push(...nodes2);
33000
+ });
32881
33001
  return nodes2;
32882
33002
  });
32883
33003
  this.mutexes.delete(txid);
@@ -32906,8 +33026,8 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
32906
33026
  if (!output) {
32907
33027
  continue;
32908
33028
  }
32909
- const parsedScript = import_btc_signer7.OutScript.decode(output.script);
32910
- const address = (0, import_btc_signer7.Address)(getNetwork(this.config.getNetwork())).encode(
33029
+ const parsedScript = import_btc_signer5.OutScript.decode(output.script);
33030
+ const address = (0, import_btc_signer5.Address)(getNetwork(this.config.getNetwork())).encode(
32911
33031
  parsedScript
32912
33032
  );
32913
33033
  const unusedDepositAddress = unusedDepositAddresses.get(address);
@@ -33054,8 +33174,7 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
33054
33174
  `TreeNode group at index ${groupIndex} not found for amount ${amount} after selection`
33055
33175
  );
33056
33176
  }
33057
- let available = await this.checkRefreshTimelockNodes(group);
33058
- available = await this.checkExtendTimeLockNodes(available);
33177
+ const available = await this.checkRenewLeaves(group);
33059
33178
  if (available.length < group.length) {
33060
33179
  throw new Error(
33061
33180
  `Not enough available nodes after refresh/extend. Expected ${group.length}, got ${available.length}`
@@ -33098,7 +33217,7 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
33098
33217
  transfer.id
33099
33218
  );
33100
33219
  if (pending) {
33101
- await this.claimTransfer({ transfer: pending, optimize: true });
33220
+ await this.claimTransfer({ transfer: pending });
33102
33221
  }
33103
33222
  }
33104
33223
  return {
@@ -33148,75 +33267,45 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
33148
33267
  newKeyDerivation: { type: "random" /* RANDOM */ }
33149
33268
  };
33150
33269
  }
33151
- async checkExtendTimeLockNodes(nodes) {
33152
- const nodesToExtend = [];
33270
+ async checkRenewLeaves(nodes) {
33271
+ const nodesToRenewNode = [];
33272
+ const nodesToRenewRefund = [];
33273
+ const nodesToRenewZeroTimelock = [];
33153
33274
  const nodeIds = [];
33154
33275
  const validNodes = [];
33155
33276
  for (const node of nodes) {
33156
33277
  const nodeTx = getTxFromRawTxBytes(node.nodeTx);
33157
- const sequence = nodeTx.getInput(0).sequence;
33158
- if (!sequence) {
33278
+ const refundTx = getTxFromRawTxBytes(node.refundTx);
33279
+ const nodeSequence = nodeTx.getInput(0).sequence;
33280
+ const refundSequence = refundTx.getInput(0).sequence;
33281
+ if (nodeSequence === void 0) {
33159
33282
  throw new ValidationError("Invalid node transaction", {
33160
33283
  field: "sequence",
33161
33284
  value: nodeTx.getInput(0),
33162
33285
  expected: "Non-null sequence"
33163
33286
  });
33164
33287
  }
33165
- const needsRefresh = doesLeafNeedRefresh(sequence, true);
33166
- if (needsRefresh) {
33167
- nodesToExtend.push(node);
33168
- nodeIds.push(node.id);
33169
- } else {
33170
- validNodes.push(node);
33171
- }
33172
- }
33173
- if (nodesToExtend.length === 0) {
33174
- return validNodes;
33175
- }
33176
- const nodesToAdd = [];
33177
- for (const node of nodesToExtend) {
33178
- const { nodes: nodes2 } = await this.transferService.extendTimelock(node);
33179
- this.leaves = this.leaves.filter((leaf) => leaf.id !== node.id);
33180
- const newNodes = await this.transferLeavesToSelf(nodes2, {
33181
- type: "leaf" /* LEAF */,
33182
- path: node.id
33183
- });
33184
- nodesToAdd.push(...newNodes);
33185
- }
33186
- this.updateLeaves(nodeIds, nodesToAdd);
33187
- validNodes.push(...nodesToAdd);
33188
- return validNodes;
33189
- }
33190
- /**
33191
- * Internal method to refresh timelock nodes.
33192
- *
33193
- * @param {string} nodeId - The optional ID of the node to refresh. If not provided, all nodes will be checked.
33194
- * @returns {Promise<void>}
33195
- * @private
33196
- */
33197
- async checkRefreshTimelockNodes(nodes) {
33198
- const nodesToRefresh = [];
33199
- const nodeIds = [];
33200
- const validNodes = [];
33201
- for (const node of nodes) {
33202
- const refundTx = getTxFromRawTxBytes(node.refundTx);
33203
- const sequence = refundTx.getInput(0).sequence;
33204
- if (!sequence) {
33288
+ if (!refundSequence) {
33205
33289
  throw new ValidationError("Invalid refund transaction", {
33206
33290
  field: "sequence",
33207
33291
  value: refundTx.getInput(0),
33208
33292
  expected: "Non-null sequence"
33209
33293
  });
33210
33294
  }
33211
- const needsRefresh = doesLeafNeedRefresh(sequence);
33212
- if (needsRefresh) {
33213
- nodesToRefresh.push(node);
33295
+ if (doesTxnNeedRenewed(refundSequence)) {
33296
+ if (isZeroTimelock(nodeSequence)) {
33297
+ nodesToRenewZeroTimelock.push(node);
33298
+ } else if (doesTxnNeedRenewed(nodeSequence)) {
33299
+ nodesToRenewNode.push(node);
33300
+ } else {
33301
+ nodesToRenewRefund.push(node);
33302
+ }
33214
33303
  nodeIds.push(node.id);
33215
33304
  } else {
33216
33305
  validNodes.push(node);
33217
33306
  }
33218
33307
  }
33219
- if (nodesToRefresh.length === 0) {
33308
+ if (nodesToRenewNode.length === 0 && nodesToRenewRefund.length === 0 && nodesToRenewZeroTimelock.length === 0) {
33220
33309
  return validNodes;
33221
33310
  }
33222
33311
  const nodesResp = await this.queryNodes({
@@ -33234,7 +33323,7 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
33234
33323
  nodesMap.set(node.id, node);
33235
33324
  }
33236
33325
  const nodesToAdd = [];
33237
- for (const node of nodesToRefresh) {
33326
+ for (const node of nodesToRenewNode) {
33238
33327
  if (!node.parentNodeId) {
33239
33328
  throw new Error(`node ${node.id} has no parent`);
33240
33329
  }
@@ -33242,17 +33331,25 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
33242
33331
  if (!parentNode) {
33243
33332
  throw new Error(`parent node ${node.parentNodeId} not found`);
33244
33333
  }
33245
- const { nodes: nodes2 } = await this.transferService.refreshTimelockNodes(
33334
+ const newNode = await this.transferService.renewNodeTxn(node, parentNode);
33335
+ nodesToAdd.push(newNode);
33336
+ }
33337
+ for (const node of nodesToRenewRefund) {
33338
+ if (!node.parentNodeId) {
33339
+ throw new Error(`node ${node.id} has no parent`);
33340
+ }
33341
+ const parentNode = nodesMap.get(node.parentNodeId);
33342
+ if (!parentNode) {
33343
+ throw new Error(`parent node ${node.parentNodeId} not found`);
33344
+ }
33345
+ const newNode = await this.transferService.renewRefundTxn(
33246
33346
  node,
33247
33347
  parentNode
33248
33348
  );
33249
- if (nodes2.length !== 1) {
33250
- throw new Error(`expected 1 node, got ${nodes2.length}`);
33251
- }
33252
- const newNode = nodes2[0];
33253
- if (!newNode) {
33254
- throw new Error("Failed to refresh timelock node");
33255
- }
33349
+ nodesToAdd.push(newNode);
33350
+ }
33351
+ for (const node of nodesToRenewZeroTimelock) {
33352
+ const newNode = await this.transferService.renewZeroTimelockNodeTxn(node);
33256
33353
  nodesToAdd.push(newNode);
33257
33354
  }
33258
33355
  this.updateLeaves(nodeIds, nodesToAdd);
@@ -33293,14 +33390,14 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
33293
33390
  return response.nodes;
33294
33391
  });
33295
33392
  }
33296
- async processClaimedTransferResults(result, transfer, emit, optimize) {
33297
- result = await this.checkRefreshTimelockNodes(result);
33298
- result = await this.checkExtendTimeLockNodes(result);
33393
+ async processClaimedTransferResults(result, transfer, emit) {
33394
+ result = await this.checkRenewLeaves(result);
33299
33395
  const existingIds = new Set(this.leaves.map((leaf) => leaf.id));
33300
33396
  const uniqueResults = result.filter((node) => !existingIds.has(node.id));
33301
33397
  this.leaves.push(...uniqueResults);
33302
- if (optimize && transfer.type !== 40 /* COUNTER_SWAP */) {
33303
- await this.optimizeLeaves();
33398
+ if (this.config.getOptimizationOptions().auto && transfer.type !== 40 /* COUNTER_SWAP */) {
33399
+ for await (const _ of this.optimizeLeaves()) {
33400
+ }
33304
33401
  }
33305
33402
  if (emit) {
33306
33403
  this.emit(
@@ -33319,8 +33416,7 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
33319
33416
  */
33320
33417
  async claimTransfer({
33321
33418
  transfer,
33322
- emit,
33323
- optimize
33419
+ emit
33324
33420
  }) {
33325
33421
  const onError = async (context) => {
33326
33422
  const error = context.error;
@@ -33365,12 +33461,7 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
33365
33461
  if (result.length === 0) {
33366
33462
  return [];
33367
33463
  }
33368
- return await this.processClaimedTransferResults(
33369
- result,
33370
- transfer,
33371
- emit,
33372
- optimize
33373
- );
33464
+ return await this.processClaimedTransferResults(result, transfer, emit);
33374
33465
  } catch (error) {
33375
33466
  console.warn(
33376
33467
  `Failed to claim transfer after all retries. Please try reinitializing your wallet in a few minutes. Transfer ID: ${transfer.id}`,
@@ -33403,7 +33494,7 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
33403
33494
  continue;
33404
33495
  }
33405
33496
  promises.push(
33406
- this.claimTransfer({ transfer, emit, optimize: true }).then(() => transfer.id).catch((error) => {
33497
+ this.claimTransfer({ transfer, emit }).then(() => transfer.id).catch((error) => {
33407
33498
  console.warn(`Failed to claim transfer ${transfer.id}:`, error);
33408
33499
  return null;
33409
33500
  })
@@ -33674,8 +33765,7 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
33674
33765
  selectedLeaves,
33675
33766
  `no leaves for ${totalAmount}`
33676
33767
  );
33677
- leaves = await this.checkRefreshTimelockNodes(leaves);
33678
- leaves = await this.checkExtendTimeLockNodes(leaves);
33768
+ leaves = await this.checkRenewLeaves(leaves);
33679
33769
  const leavesToSend = await Promise.all(
33680
33770
  leaves.map(async (leaf) => ({
33681
33771
  leaf,
@@ -34006,10 +34096,8 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
34006
34096
  leavesToSendToSsp = leavesForTargetAmount;
34007
34097
  leavesToSendToSE = leavesForFee;
34008
34098
  }
34009
- leavesToSendToSsp = await this.checkRefreshTimelockNodes(leavesToSendToSsp);
34010
- leavesToSendToSsp = await this.checkExtendTimeLockNodes(leavesToSendToSsp);
34011
- leavesToSendToSE = await this.checkRefreshTimelockNodes(leavesToSendToSE);
34012
- leavesToSendToSE = await this.checkExtendTimeLockNodes(leavesToSendToSE);
34099
+ leavesToSendToSsp = await this.checkRenewLeaves(leavesToSendToSsp);
34100
+ leavesToSendToSE = await this.checkRenewLeaves(leavesToSendToSE);
34013
34101
  const leafKeyTweaks = await Promise.all(
34014
34102
  [...leavesToSendToSE, ...leavesToSendToSsp].map(async (leaf) => ({
34015
34103
  leaf,
@@ -34095,8 +34183,7 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
34095
34183
  (await this.selectLeaves([amountSats])).get(amountSats),
34096
34184
  `no leaves for ${amountSats}`
34097
34185
  );
34098
- leaves = await this.checkRefreshTimelockNodes(leaves);
34099
- leaves = await this.checkExtendTimeLockNodes(leaves);
34186
+ leaves = await this.checkRenewLeaves(leaves);
34100
34187
  const feeEstimate = await sspClient.getCoopExitFeeQuote({
34101
34188
  leafExternalIds: leaves.map((leaf) => leaf.id),
34102
34189
  withdrawalAddress
@@ -34384,7 +34471,7 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
34384
34471
  */
34385
34472
  async signTransaction(txHex, keyType = "auto-detect") {
34386
34473
  try {
34387
- const tx = import_btc_signer7.Transaction.fromRaw((0, import_utils25.hexToBytes)(txHex));
34474
+ const tx = import_btc_signer5.Transaction.fromRaw((0, import_utils25.hexToBytes)(txHex));
34388
34475
  let publicKey;
34389
34476
  switch (keyType.toLowerCase()) {
34390
34477
  case "identity":
@@ -34591,15 +34678,6 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
34591
34678
  }
34592
34679
  );
34593
34680
  }
34594
- if (!nodeInput.sequence) {
34595
- throw new ValidationError(
34596
- `Node transaction has no sequence for ${isRootNode ? "root" : "non-root"} node`,
34597
- {
34598
- field: "sequence",
34599
- value: nodeInput.sequence
34600
- }
34601
- );
34602
- }
34603
34681
  const refundInput = refundTx.getInput(0);
34604
34682
  if (!refundInput) {
34605
34683
  throw new ValidationError(
@@ -34635,89 +34713,6 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
34635
34713
  );
34636
34714
  }
34637
34715
  }
34638
- /**
34639
- * Refresh the timelock of a specific node.
34640
- *
34641
- * @param {string} nodeId - The ID of the node to refresh
34642
- * @returns {Promise<void>} Promise that resolves when the timelock is refreshed
34643
- */
34644
- async testOnly_expireTimelock(nodeId) {
34645
- const sparkClient = await this.connectionManager.createSparkClient(
34646
- this.config.getCoordinatorAddress()
34647
- );
34648
- try {
34649
- const response = await sparkClient.query_nodes({
34650
- source: {
34651
- $case: "nodeIds",
34652
- nodeIds: {
34653
- nodeIds: [nodeId]
34654
- }
34655
- },
34656
- includeParents: true
34657
- });
34658
- let leaf = response.nodes[nodeId];
34659
- if (!leaf) {
34660
- throw new ValidationError("Node not found", {
34661
- field: "nodeId",
34662
- value: nodeId
34663
- });
34664
- }
34665
- let parentNode;
34666
- let hasParentNode = false;
34667
- if (!leaf.parentNodeId) {
34668
- } else {
34669
- hasParentNode = true;
34670
- parentNode = response.nodes[leaf.parentNodeId];
34671
- if (!parentNode) {
34672
- throw new ValidationError("Parent node not found", {
34673
- field: "parentNodeId",
34674
- value: leaf.parentNodeId
34675
- });
34676
- }
34677
- }
34678
- const nodeTx = getTxFromRawTxBytes(leaf.nodeTx);
34679
- const refundTx = getTxFromRawTxBytes(leaf.refundTx);
34680
- if (hasParentNode) {
34681
- const nodeTimelock = getCurrentTimelock(nodeTx.getInput(0).sequence);
34682
- if (nodeTimelock > 100) {
34683
- const expiredNodeTxLeaf = await this.transferService.testonly_expireTimeLockNodeTx(
34684
- leaf,
34685
- parentNode
34686
- );
34687
- if (!expiredNodeTxLeaf.nodes[0]) {
34688
- throw new ValidationError("No expired node tx leaf", {
34689
- field: "expiredNodeTxLeaf",
34690
- value: expiredNodeTxLeaf
34691
- });
34692
- }
34693
- leaf = expiredNodeTxLeaf.nodes[0];
34694
- }
34695
- }
34696
- const refundTimelock = getCurrentTimelock(refundTx.getInput(0).sequence);
34697
- if (refundTimelock > 100) {
34698
- const expiredTxLeaf = await this.transferService.testonly_expireTimeLockRefundtx(leaf);
34699
- if (!expiredTxLeaf.nodes[0]) {
34700
- throw new ValidationError("No expired tx leaf", {
34701
- field: "expiredTxLeaf",
34702
- value: expiredTxLeaf
34703
- });
34704
- }
34705
- leaf = expiredTxLeaf.nodes[0];
34706
- }
34707
- const leafIndex = this.leaves.findIndex((leaf2) => leaf2.id === leaf2.id);
34708
- if (leafIndex !== -1) {
34709
- this.leaves[leafIndex] = leaf;
34710
- }
34711
- } catch (error) {
34712
- throw new NetworkError(
34713
- "Failed to refresh timelock",
34714
- {
34715
- method: "refresh_timelock"
34716
- },
34717
- error
34718
- );
34719
- }
34720
- }
34721
34716
  cleanup() {
34722
34717
  if (this.claimTransfersInterval) {
34723
34718
  clearInterval(this.claimTransfersInterval);
@@ -34872,8 +34867,7 @@ var SparkWallet = class extends import_eventemitter3.EventEmitter {
34872
34867
  "getLightningReceiveRequest",
34873
34868
  "getLightningSendRequest",
34874
34869
  "getCoopExitRequest",
34875
- "checkTimelock",
34876
- "testOnly_expireTimelock"
34870
+ "checkTimelock"
34877
34871
  ];
34878
34872
  methods.forEach((m) => this.wrapPublicSparkWalletMethodWithOtelSpan(m));
34879
34873
  this.initWallet = this.wrapWithOtelSpan(
@@ -35873,7 +35867,6 @@ setCrypto(cryptoImpl2);
35873
35867
  DIRECT_TIMELOCK_OFFSET,
35874
35868
  DefaultSparkSigner,
35875
35869
  HTLC_TIMELOCK_OFFSET,
35876
- INITIAL_DIRECT_SEQUENCE,
35877
35870
  INITIAL_SEQUENCE,
35878
35871
  InternalValidationError,
35879
35872
  KeyDerivationType,
@@ -35910,21 +35903,24 @@ setCrypto(cryptoImpl2);
35910
35903
  constructFeeBumpTx,
35911
35904
  constructUnilateralExitFeeBumpPackages,
35912
35905
  constructUnilateralExitTxs,
35913
- createConnectorRefundTransactions,
35914
- createLeafNodeTx,
35915
- createNodeTx,
35916
- createNodeTxs,
35917
- createRefundTx,
35918
- createRefundTxs,
35919
- createRootTx,
35906
+ createConnectorRefundTxs,
35907
+ createCurrentTimelockRefundTxs,
35908
+ createDecrementedTimelockNodeTx,
35909
+ createDecrementedTimelockRefundTxs,
35910
+ createInitialTimelockNodeTx,
35911
+ createInitialTimelockRefundTxs,
35912
+ createRootNodeTx,
35920
35913
  createSigningCommitment,
35921
35914
  createSigningNonce,
35922
- createSplitTx,
35915
+ createTestUnilateralRefundTxs,
35916
+ createTestUnilateralTimelockNodeTx,
35917
+ createZeroTimelockNodeTx,
35923
35918
  decodeBech32mTokenIdentifier,
35924
35919
  decodeBytesToSigningCommitment,
35925
35920
  decodeBytesToSigningNonce,
35926
35921
  decodeSparkAddress,
35927
35922
  doesLeafNeedRefresh,
35923
+ doesTxnNeedRenewed,
35928
35924
  encodeBech32mTokenIdentifier,
35929
35925
  encodeSigningCommitmentToBytes,
35930
35926
  encodeSigningNonceToBytes,
@@ -35962,6 +35958,7 @@ setCrypto(cryptoImpl2);
35962
35958
  getTxFromRawTxHex,
35963
35959
  getTxId,
35964
35960
  getTxIdNoReverse,
35961
+ hash160,
35965
35962
  initializeTracerEnv,
35966
35963
  isEphemeralAnchorOutput,
35967
35964
  isLegacySparkAddress,
@@ -35969,6 +35966,7 @@ setCrypto(cryptoImpl2);
35969
35966
  isTxBroadcast,
35970
35967
  isValidPublicKey,
35971
35968
  isValidSparkAddress,
35969
+ isZeroTimelock,
35972
35970
  lastKeyWithTarget,
35973
35971
  maybeApplyFee,
35974
35972
  modInverse,