@buildonspark/spark-sdk 0.1.38 → 0.1.40

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 (104) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/android/build/intermediates/incremental/mergeDebugJniLibFolders/merger.xml +1 -1
  3. package/android/build/intermediates/library_jni/debug/copyDebugJniLibsProjectOnly/jni/arm64-v8a/libspark_frost.so +0 -0
  4. package/android/build/intermediates/library_jni/debug/copyDebugJniLibsProjectOnly/jni/arm64-v8a/libuniffi_spark_frost.so +0 -0
  5. package/android/build/intermediates/library_jni/debug/copyDebugJniLibsProjectOnly/jni/armeabi-v7a/libspark_frost.so +0 -0
  6. package/android/build/intermediates/library_jni/debug/copyDebugJniLibsProjectOnly/jni/armeabi-v7a/libuniffi_spark_frost.so +0 -0
  7. package/android/build/intermediates/library_jni/debug/copyDebugJniLibsProjectOnly/jni/x86/libspark_frost.so +0 -0
  8. package/android/build/intermediates/library_jni/debug/copyDebugJniLibsProjectOnly/jni/x86/libuniffi_spark_frost.so +0 -0
  9. package/android/build/intermediates/library_jni/debug/copyDebugJniLibsProjectOnly/jni/x86_64/libspark_frost.so +0 -0
  10. package/android/build/intermediates/library_jni/debug/copyDebugJniLibsProjectOnly/jni/x86_64/libuniffi_spark_frost.so +0 -0
  11. package/android/build/intermediates/merged_jni_libs/debug/mergeDebugJniLibFolders/out/arm64-v8a/libspark_frost.so +0 -0
  12. package/android/build/intermediates/merged_jni_libs/debug/mergeDebugJniLibFolders/out/arm64-v8a/libuniffi_spark_frost.so +0 -0
  13. package/android/build/intermediates/merged_jni_libs/debug/mergeDebugJniLibFolders/out/armeabi-v7a/libspark_frost.so +0 -0
  14. package/android/build/intermediates/merged_jni_libs/debug/mergeDebugJniLibFolders/out/armeabi-v7a/libuniffi_spark_frost.so +0 -0
  15. package/android/build/intermediates/merged_jni_libs/debug/mergeDebugJniLibFolders/out/x86/libspark_frost.so +0 -0
  16. package/android/build/intermediates/merged_jni_libs/debug/mergeDebugJniLibFolders/out/x86/libuniffi_spark_frost.so +0 -0
  17. package/android/build/intermediates/merged_jni_libs/debug/mergeDebugJniLibFolders/out/x86_64/libspark_frost.so +0 -0
  18. package/android/build/intermediates/merged_jni_libs/debug/mergeDebugJniLibFolders/out/x86_64/libuniffi_spark_frost.so +0 -0
  19. package/android/build/intermediates/merged_native_libs/debug/mergeDebugNativeLibs/out/lib/arm64-v8a/libspark_frost.so +0 -0
  20. package/android/build/intermediates/merged_native_libs/debug/mergeDebugNativeLibs/out/lib/arm64-v8a/libuniffi_spark_frost.so +0 -0
  21. package/android/build/intermediates/merged_native_libs/debug/mergeDebugNativeLibs/out/lib/armeabi-v7a/libspark_frost.so +0 -0
  22. package/android/build/intermediates/merged_native_libs/debug/mergeDebugNativeLibs/out/lib/armeabi-v7a/libuniffi_spark_frost.so +0 -0
  23. package/android/build/intermediates/merged_native_libs/debug/mergeDebugNativeLibs/out/lib/x86/libspark_frost.so +0 -0
  24. package/android/build/intermediates/merged_native_libs/debug/mergeDebugNativeLibs/out/lib/x86/libuniffi_spark_frost.so +0 -0
  25. package/android/build/intermediates/merged_native_libs/debug/mergeDebugNativeLibs/out/lib/x86_64/libspark_frost.so +0 -0
  26. package/android/build/intermediates/merged_native_libs/debug/mergeDebugNativeLibs/out/lib/x86_64/libuniffi_spark_frost.so +0 -0
  27. package/dist/{RequestLightningSendInput-B4JdzclX.d.ts → RequestLightningSendInput-CJtcHOnu.d.ts} +1 -1
  28. package/dist/{RequestLightningSendInput-39_zGri6.d.cts → RequestLightningSendInput-DfmfqzZo.d.cts} +1 -1
  29. package/dist/address/index.d.cts +1 -1
  30. package/dist/address/index.d.ts +1 -1
  31. package/dist/address/index.js +2 -2
  32. package/dist/{chunk-W3EC5XSA.js → chunk-5MNQB2T4.js} +2 -2
  33. package/dist/chunk-ED3ZAFDI.js +784 -0
  34. package/dist/{chunk-VJTDG4BQ.js → chunk-HK6LPV6Z.js} +10 -1
  35. package/dist/{chunk-7WRK6WNJ.js → chunk-LHT4QTFK.js} +556 -41
  36. package/dist/{chunk-RAPBVYJY.js → chunk-RFCXPGDM.js} +26 -4
  37. package/dist/{chunk-DI7QXUQJ.js → chunk-W2VXS35Y.js} +4 -4
  38. package/dist/graphql/objects/index.d.cts +5 -4
  39. package/dist/graphql/objects/index.d.ts +5 -4
  40. package/dist/{index-CxAi2L8y.d.ts → index-BDEYgYxP.d.ts} +42 -4
  41. package/dist/{index-Dm17Ggfe.d.cts → index-CLdtdMU4.d.cts} +42 -4
  42. package/dist/index.cjs +1069 -40
  43. package/dist/index.d.cts +6 -6
  44. package/dist/index.d.ts +6 -6
  45. package/dist/index.js +33 -17
  46. package/dist/index.node.cjs +1069 -40
  47. package/dist/index.node.d.cts +6 -6
  48. package/dist/index.node.d.ts +6 -6
  49. package/dist/index.node.js +33 -17
  50. package/dist/native/index.cjs +1069 -40
  51. package/dist/native/index.d.cts +108 -5
  52. package/dist/native/index.d.ts +108 -5
  53. package/dist/native/index.js +1065 -40
  54. package/dist/{network-GFGEHkS4.d.cts → network-B10hBoHp.d.cts} +8 -1
  55. package/dist/{network-DobHpaV6.d.ts → network-CCgyIsGl.d.ts} +8 -1
  56. package/dist/services/config.cjs +29 -12
  57. package/dist/services/config.d.cts +4 -4
  58. package/dist/services/config.d.ts +4 -4
  59. package/dist/services/config.js +5 -5
  60. package/dist/services/connection.d.cts +4 -4
  61. package/dist/services/connection.d.ts +4 -4
  62. package/dist/services/connection.js +2 -2
  63. package/dist/services/index.cjs +30 -13
  64. package/dist/services/index.d.cts +4 -4
  65. package/dist/services/index.d.ts +4 -4
  66. package/dist/services/index.js +8 -8
  67. package/dist/services/lrc-connection.d.cts +4 -4
  68. package/dist/services/lrc-connection.d.ts +4 -4
  69. package/dist/services/lrc-connection.js +1 -1
  70. package/dist/services/token-transactions.cjs +1 -1
  71. package/dist/services/token-transactions.d.cts +4 -4
  72. package/dist/services/token-transactions.d.ts +4 -4
  73. package/dist/services/token-transactions.js +3 -3
  74. package/dist/services/wallet-config.d.cts +4 -4
  75. package/dist/services/wallet-config.d.ts +4 -4
  76. package/dist/signer/signer.cjs +23 -6
  77. package/dist/signer/signer.d.cts +3 -2
  78. package/dist/signer/signer.d.ts +3 -2
  79. package/dist/signer/signer.js +1 -1
  80. package/dist/{signer-DFGw9RRp.d.ts → signer-C5h1DpjF.d.ts} +4 -1
  81. package/dist/{signer-C1t40Wus.d.cts → signer-CYwn7h9U.d.cts} +4 -1
  82. package/dist/types/index.d.cts +4 -3
  83. package/dist/types/index.d.ts +4 -3
  84. package/dist/utils/index.cjs +891 -2
  85. package/dist/utils/index.d.cts +62 -6
  86. package/dist/utils/index.d.ts +62 -6
  87. package/dist/utils/index.js +23 -7
  88. package/package.json +1 -1
  89. package/src/services/deposit.ts +23 -5
  90. package/src/services/token-transactions.ts +1 -1
  91. package/src/services/transfer.ts +218 -11
  92. package/src/services/tree-creation.ts +29 -14
  93. package/src/signer/signer.ts +47 -5
  94. package/src/spark-wallet/spark-wallet.ts +430 -4
  95. package/src/tests/integration/swap.test.ts +225 -0
  96. package/src/tests/integration/tree-creation.test.ts +5 -1
  97. package/src/utils/index.ts +1 -0
  98. package/src/utils/mempool.ts +26 -1
  99. package/src/utils/network.ts +15 -0
  100. package/src/utils/transaction.ts +22 -2
  101. package/src/utils/unilateral-exit.ts +729 -0
  102. package/dist/chunk-E5SL7XTO.js +0 -301
  103. package/dist/{chunk-LIP2K6KR.js → chunk-2CDJZQN4.js} +3 -3
  104. package/dist/{chunk-RGWBSZIO.js → chunk-I4JI6TYN.js} +4 -4
@@ -1290,6 +1290,7 @@ var index_exports = {};
1290
1290
  __export(index_exports, {
1291
1291
  AuthenticationError: () => AuthenticationError,
1292
1292
  ConfigurationError: () => ConfigurationError,
1293
+ DEFAULT_FEE_SATS: () => DEFAULT_FEE_SATS,
1293
1294
  DefaultSparkSigner: () => ReactNativeSparkSigner,
1294
1295
  InternalValidationError: () => InternalValidationError,
1295
1296
  LRC_WALLET_NETWORK: () => LRC_WALLET_NETWORK,
@@ -1313,6 +1314,9 @@ __export(index_exports, {
1313
1314
  collectResponses: () => collectResponses,
1314
1315
  computeTaprootKeyNoScript: () => computeTaprootKeyNoScript,
1315
1316
  computerLagrangeCoefficients: () => computerLagrangeCoefficients,
1317
+ constructFeeBumpTx: () => constructFeeBumpTx,
1318
+ constructUnilateralExitFeeBumpPackages: () => constructUnilateralExitFeeBumpPackages,
1319
+ constructUnilateralExitTxs: () => constructUnilateralExitTxs,
1316
1320
  createRefundTx: () => createRefundTx,
1317
1321
  createSigningCommitment: () => createSigningCommitment,
1318
1322
  createSigningNonce: () => createSigningNonce,
@@ -1331,6 +1335,7 @@ __export(index_exports, {
1331
1335
  getLatestDepositTxId: () => getLatestDepositTxId,
1332
1336
  getNetwork: () => getNetwork,
1333
1337
  getNetworkFromAddress: () => getNetworkFromAddress,
1338
+ getNetworkFromString: () => getNetworkFromString,
1334
1339
  getNextTransactionSequence: () => getNextTransactionSequence,
1335
1340
  getP2TRAddressFromPkScript: () => getP2TRAddressFromPkScript,
1336
1341
  getP2TRAddressFromPublicKey: () => getP2TRAddressFromPublicKey,
@@ -1346,7 +1351,10 @@ __export(index_exports, {
1346
1351
  getTxFromRawTxHex: () => getTxFromRawTxHex,
1347
1352
  getTxId: () => getTxId,
1348
1353
  getTxIdNoReverse: () => getTxIdNoReverse,
1354
+ isEphemeralAnchorOutput: () => isEphemeralAnchorOutput,
1355
+ isTxBroadcast: () => isTxBroadcast,
1349
1356
  lastKeyWithTarget: () => lastKeyWithTarget,
1357
+ maybeApplyFee: () => maybeApplyFee,
1350
1358
  modInverse: () => modInverse,
1351
1359
  proofOfPossessionMessageHashForDepositAddress: () => proofOfPossessionMessageHashForDepositAddress,
1352
1360
  recoverSecret: () => recoverSecret,
@@ -2106,11 +2114,9 @@ function decodeBytesToSigningCommitment(bytes2) {
2106
2114
  }
2107
2115
 
2108
2116
  // src/signer/signer.ts
2109
- var import_lrc20_sdk = require("@buildonspark/lrc20-sdk");
2110
- var import_lrc20_sdk2 = require("@buildonspark/lrc20-sdk");
2111
2117
  var import_secp256k16 = require("@bitcoinerlab/secp256k1");
2118
+ var import_lrc20_sdk = require("@buildonspark/lrc20-sdk");
2112
2119
  var import_sha2 = require("@noble/hashes/sha2");
2113
- var import_lrc20_sdk3 = require("@buildonspark/lrc20-sdk");
2114
2120
  var sparkFrostModule = void 0;
2115
2121
  var getSparkFrostModule = async () => {
2116
2122
  if (isReactNative) {
@@ -2576,16 +2582,16 @@ var DefaultSparkSigner = class {
2576
2582
  }
2577
2583
  if (receipt) {
2578
2584
  const receiptPrivateKey = this.getReceiptPrivateKey(receipt);
2579
- const tweakedKeyPair = (0, import_lrc20_sdk3.fromPrivateKey)(import_buffer.Buffer.from(receiptPrivateKey));
2585
+ const tweakedKeyPair = (0, import_lrc20_sdk.fromPrivateKey)(import_buffer.Buffer.from(receiptPrivateKey));
2580
2586
  psbt.signInput(input, tweakedKeyPair, sighashTypes);
2581
2587
  return psbt;
2582
2588
  }
2583
- const keypair = (0, import_lrc20_sdk3.fromPrivateKey)(import_buffer.Buffer.from(this.identityKey.privateKey));
2589
+ const keypair = (0, import_lrc20_sdk.fromPrivateKey)(import_buffer.Buffer.from(this.identityKey.privateKey));
2584
2590
  psbt.signInput(input, keypair, sighashTypes);
2585
2591
  return psbt;
2586
2592
  }
2587
2593
  getReceiptPrivateKey(receipt) {
2588
- const pxh = import_lrc20_sdk2.Receipt.receiptHash(receipt);
2594
+ const pxh = import_lrc20_sdk.Receipt.receiptHash(receipt);
2589
2595
  let innerKey = this.identityKey.publicKey;
2590
2596
  let privateKey = this.identityKey.privateKey;
2591
2597
  if (innerKey[0] === 3) {
@@ -2596,6 +2602,25 @@ var DefaultSparkSigner = class {
2596
2602
  const receiptProof = (0, import_secp256k16.privateAdd)(privateKey, pxhPubkey);
2597
2603
  return import_buffer.Buffer.from(receiptProof);
2598
2604
  }
2605
+ signTransactionIndex(tx, index, publicKey) {
2606
+ let privateKey;
2607
+ if ((0, import_utils4.equalBytes)(publicKey, this.identityKey?.publicKey ?? new Uint8Array())) {
2608
+ privateKey = this.identityKey?.privateKey;
2609
+ } else if ((0, import_utils4.equalBytes)(publicKey, this.depositKey?.publicKey ?? new Uint8Array())) {
2610
+ privateKey = this.depositKey?.privateKey;
2611
+ } else {
2612
+ privateKey = (0, import_utils4.hexToBytes)(
2613
+ this.publicKeyToPrivateKeyMap.get((0, import_utils4.bytesToHex)(publicKey)) ?? ""
2614
+ );
2615
+ }
2616
+ if (!privateKey) {
2617
+ throw new ValidationError("Private key not found for public key", {
2618
+ field: "privateKey",
2619
+ value: (0, import_utils4.bytesToHex)(publicKey)
2620
+ });
2621
+ }
2622
+ tx.signIdx(privateKey, index);
2623
+ }
2599
2624
  };
2600
2625
 
2601
2626
  // src/signer/signer.react-native.ts
@@ -17706,7 +17731,7 @@ init_buffer();
17706
17731
 
17707
17732
  // src/utils/network.ts
17708
17733
  init_buffer();
17709
- var import_lrc20_sdk4 = require("@buildonspark/lrc20-sdk");
17734
+ var import_lrc20_sdk2 = require("@buildonspark/lrc20-sdk");
17710
17735
  var btc = __toESM(require("@scure/btc-signer"), 1);
17711
17736
  var bitcoin = __toESM(require("bitcoinjs-lib"), 1);
17712
17737
  var Network2 = /* @__PURE__ */ ((Network5) => {
@@ -17740,11 +17765,11 @@ var LRC_WALLET_NETWORK = Object.freeze({
17740
17765
  [4 /* LOCAL */]: bitcoin.networks.regtest
17741
17766
  });
17742
17767
  var LRC_WALLET_NETWORK_TYPE = Object.freeze({
17743
- [0 /* MAINNET */]: import_lrc20_sdk4.NetworkType.MAINNET,
17744
- [1 /* TESTNET */]: import_lrc20_sdk4.NetworkType.TESTNET,
17745
- [2 /* SIGNET */]: import_lrc20_sdk4.NetworkType.TESTNET,
17746
- [3 /* REGTEST */]: import_lrc20_sdk4.NetworkType.REGTEST,
17747
- [4 /* LOCAL */]: import_lrc20_sdk4.NetworkType.LOCAL
17768
+ [0 /* MAINNET */]: import_lrc20_sdk2.NetworkType.MAINNET,
17769
+ [1 /* TESTNET */]: import_lrc20_sdk2.NetworkType.TESTNET,
17770
+ [2 /* SIGNET */]: import_lrc20_sdk2.NetworkType.TESTNET,
17771
+ [3 /* REGTEST */]: import_lrc20_sdk2.NetworkType.REGTEST,
17772
+ [4 /* LOCAL */]: import_lrc20_sdk2.NetworkType.LOCAL
17748
17773
  });
17749
17774
  function getNetworkFromAddress(address2) {
17750
17775
  try {
@@ -17767,6 +17792,14 @@ function getNetworkFromAddress(address2) {
17767
17792
  }
17768
17793
  return null;
17769
17794
  }
17795
+ function getNetworkFromString(network) {
17796
+ const net = (network ?? "REGTEST").toUpperCase();
17797
+ if (net === "MAINNET") return 0 /* MAINNET */;
17798
+ if (net === "TESTNET") return 1 /* TESTNET */;
17799
+ if (net === "SIGNET") return 2 /* SIGNET */;
17800
+ if (net === "LOCAL") return 4 /* LOCAL */;
17801
+ return 3 /* REGTEST */;
17802
+ }
17770
17803
 
17771
17804
  // src/services/wallet-config.ts
17772
17805
  init_buffer();
@@ -19324,8 +19357,20 @@ function getTxIdNoReverse(tx) {
19324
19357
  init_buffer();
19325
19358
  var import_btc_signer = require("@scure/btc-signer");
19326
19359
  var TIME_LOCK_INTERVAL = 100;
19360
+ var ESTIMATED_TX_SIZE = 191;
19361
+ var DEFAULT_SATS_PER_VBYTE = 5;
19362
+ var DEFAULT_FEE_SATS = ESTIMATED_TX_SIZE * DEFAULT_SATS_PER_VBYTE;
19363
+ function maybeApplyFee(amount) {
19364
+ if (amount > BigInt(DEFAULT_FEE_SATS)) {
19365
+ return amount - BigInt(DEFAULT_FEE_SATS);
19366
+ }
19367
+ return amount;
19368
+ }
19327
19369
  function createRefundTx(sequence, nodeOutPoint, amountSats, receivingPubkey, network) {
19328
- const newRefundTx = new import_btc_signer.Transaction({ allowUnknownOutputs: true });
19370
+ const newRefundTx = new import_btc_signer.Transaction({
19371
+ version: 3,
19372
+ allowUnknownOutputs: true
19373
+ });
19329
19374
  newRefundTx.addInput({
19330
19375
  ...nodeOutPoint,
19331
19376
  sequence
@@ -19354,7 +19399,7 @@ function getNextTransactionSequence(currSequence, forRefresh) {
19354
19399
  needRefresh: true
19355
19400
  };
19356
19401
  }
19357
- if (nextTimelock <= 0) {
19402
+ if (nextTimelock < 0) {
19358
19403
  throw new ValidationError("timelock interval is less than or equal to 0", {
19359
19404
  field: "nextTimelock",
19360
19405
  value: nextTimelock
@@ -19465,6 +19510,32 @@ var BaseTransferService = class {
19465
19510
  }
19466
19511
  return updatedTransfer;
19467
19512
  }
19513
+ async deliverTransferPackage(transfer, leaves, refundSignatureMap) {
19514
+ const keyTweakInputMap = await this.prepareSendTransferKeyTweaks(
19515
+ transfer.id,
19516
+ transfer.receiverIdentityPublicKey,
19517
+ leaves,
19518
+ refundSignatureMap
19519
+ );
19520
+ const transferPackage = await this.prepareTransferPackage(
19521
+ transfer.id,
19522
+ keyTweakInputMap,
19523
+ leaves,
19524
+ transfer.receiverIdentityPublicKey
19525
+ );
19526
+ const sparkClient = await this.connectionManager.createSparkClient(
19527
+ this.config.getCoordinatorAddress()
19528
+ );
19529
+ const response = await sparkClient.finalize_transfer_with_transfer_package({
19530
+ transferId: transfer.id,
19531
+ ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
19532
+ transferPackage
19533
+ });
19534
+ if (!response.transfer) {
19535
+ throw new ValidationError("No transfer response from operator");
19536
+ }
19537
+ return response.transfer;
19538
+ }
19468
19539
  async sendTransferWithKeyTweaks(leaves, receiverIdentityPubkey) {
19469
19540
  const transferID = (0, import_uuidv7.uuidv7)();
19470
19541
  const keyTweakInputMap = await this.prepareSendTransferKeyTweaks(
@@ -20222,9 +20293,20 @@ var TransferService = class extends BaseTransferService {
20222
20293
  if (!input) {
20223
20294
  throw Error("Could not fetch tx input");
20224
20295
  }
20225
- const newTx = new import_btc_signer2.Transaction({ allowUnknownOutputs: true });
20226
- for (let j = 0; j < nodeTx.outputsLength; j++) {
20227
- newTx.addOutput(nodeTx.getOutput(j));
20296
+ const newTx = new import_btc_signer2.Transaction({ version: 3, allowUnknownOutputs: true });
20297
+ const originalOutput = nodeTx.getOutput(0);
20298
+ if (!originalOutput) {
20299
+ throw Error("Could not get original output");
20300
+ }
20301
+ newTx.addOutput({
20302
+ script: originalOutput.script,
20303
+ amount: originalOutput.amount
20304
+ });
20305
+ for (let j = 1; j < nodeTx.outputsLength; j++) {
20306
+ const additionalOutput = nodeTx.getOutput(j);
20307
+ if (additionalOutput) {
20308
+ newTx.addOutput(additionalOutput);
20309
+ }
20228
20310
  }
20229
20311
  if (i === 0) {
20230
20312
  const currSequence = input.sequence;
@@ -20251,9 +20333,23 @@ var TransferService = class extends BaseTransferService {
20251
20333
  throw Error("leaf does not have refund tx");
20252
20334
  }
20253
20335
  const refundTx = getTxFromRawTxBytes(leaf?.refundTx);
20254
- const newRefundTx = new import_btc_signer2.Transaction({ allowUnknownOutputs: true });
20255
- for (let j = 0; j < refundTx.outputsLength; j++) {
20256
- newRefundTx.addOutput(refundTx.getOutput(j));
20336
+ const newRefundTx = new import_btc_signer2.Transaction({
20337
+ version: 3,
20338
+ allowUnknownOutputs: true
20339
+ });
20340
+ const originalRefundOutput = refundTx.getOutput(0);
20341
+ if (!originalRefundOutput) {
20342
+ throw Error("Could not get original refund output");
20343
+ }
20344
+ newRefundTx.addOutput({
20345
+ script: originalRefundOutput.script,
20346
+ amount: originalRefundOutput.amount
20347
+ });
20348
+ for (let j = 1; j < refundTx.outputsLength; j++) {
20349
+ const additionalOutput = refundTx.getOutput(j);
20350
+ if (additionalOutput) {
20351
+ newRefundTx.addOutput(additionalOutput);
20352
+ }
20257
20353
  }
20258
20354
  const refundTxInput = refundTx.getInput(0);
20259
20355
  if (!refundTxInput) {
@@ -20362,10 +20458,11 @@ var TransferService = class extends BaseTransferService {
20362
20458
  nodeTxSignature: leafSignature,
20363
20459
  refundTxSignature: refundSignature
20364
20460
  });
20365
- return await sparkClient.finalize_node_signatures({
20461
+ const result = await sparkClient.finalize_node_signatures({
20366
20462
  intent: 3 /* REFRESH */,
20367
20463
  nodeSignatures
20368
20464
  });
20465
+ return result;
20369
20466
  }
20370
20467
  async extendTimelock(node, signingPubKey) {
20371
20468
  const nodeTx = getTxFromRawTxBytes(node.nodeTx);
@@ -20376,9 +20473,20 @@ var TransferService = class extends BaseTransferService {
20376
20473
  index: 0
20377
20474
  };
20378
20475
  const { nextSequence: newNodeSequence } = getNextTransactionSequence(refundSequence);
20379
- const newNodeTx = new import_btc_signer2.Transaction({ allowUnknownOutputs: true });
20476
+ const newNodeTx = new import_btc_signer2.Transaction({
20477
+ version: 3,
20478
+ allowUnknownOutputs: true
20479
+ });
20380
20480
  newNodeTx.addInput({ ...newNodeOutPoint, sequence: newNodeSequence });
20381
- newNodeTx.addOutput(nodeTx.getOutput(0));
20481
+ const originalOutput = nodeTx.getOutput(0);
20482
+ if (!originalOutput) {
20483
+ throw Error("Could not get original node output");
20484
+ }
20485
+ newNodeTx.addOutput({
20486
+ script: originalOutput.script,
20487
+ amount: originalOutput.amount
20488
+ // feeReducedAmount,
20489
+ });
20382
20490
  newNodeTx.addOutput(getEphemeralAnchorOutput());
20383
20491
  const newRefundOutPoint = {
20384
20492
  txid: (0, import_utils8.hexToBytes)(getTxId(newNodeTx)),
@@ -20392,11 +20500,16 @@ var TransferService = class extends BaseTransferService {
20392
20500
  initialSequence(),
20393
20501
  newRefundOutPoint,
20394
20502
  amountSats,
20503
+ // feeReducedRefundAmount,
20395
20504
  signingPubKey,
20396
20505
  this.config.getNetwork()
20397
20506
  );
20398
20507
  const nodeSighash = getSigHashFromTx(newNodeTx, 0, nodeTx.getOutput(0));
20399
- const refundSighash = getSigHashFromTx(newRefundTx, 0, nodeTx.getOutput(0));
20508
+ const refundSighash = getSigHashFromTx(
20509
+ newRefundTx,
20510
+ 0,
20511
+ newNodeTx.getOutput(0)
20512
+ );
20400
20513
  const newNodeSigningJob = {
20401
20514
  signingPublicKey: signingPubKey,
20402
20515
  rawTx: newNodeTx.toBytes(),
@@ -20470,6 +20583,94 @@ var TransferService = class extends BaseTransferService {
20470
20583
  ]
20471
20584
  });
20472
20585
  }
20586
+ async refreshTimelockRefundTx(node, signingPubKey) {
20587
+ const nodeTx = getTxFromRawTxBytes(node.nodeTx);
20588
+ const refundTx = getTxFromRawTxBytes(node.refundTx);
20589
+ const currSequence = refundTx.getInput(0).sequence || 0;
20590
+ const { nextSequence } = getNextTransactionSequence(currSequence);
20591
+ const newRefundTx = new import_btc_signer2.Transaction({
20592
+ version: 3,
20593
+ allowUnknownOutputs: true
20594
+ });
20595
+ const originalRefundOutput = refundTx.getOutput(0);
20596
+ if (!originalRefundOutput) {
20597
+ throw Error("Could not get original refund output");
20598
+ }
20599
+ newRefundTx.addOutput({
20600
+ script: originalRefundOutput.script,
20601
+ amount: originalRefundOutput.amount
20602
+ });
20603
+ for (let j = 1; j < refundTx.outputsLength; j++) {
20604
+ const additionalOutput = refundTx.getOutput(j);
20605
+ if (additionalOutput) {
20606
+ newRefundTx.addOutput(additionalOutput);
20607
+ }
20608
+ }
20609
+ const refundTxInput = refundTx.getInput(0);
20610
+ if (!refundTxInput) {
20611
+ throw Error("refund tx doesn't have input");
20612
+ }
20613
+ newRefundTx.addInput({
20614
+ ...refundTxInput,
20615
+ sequence: nextSequence
20616
+ });
20617
+ const refundSigningJob = {
20618
+ signingPublicKey: signingPubKey,
20619
+ rawTx: newRefundTx.toBytes(),
20620
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
20621
+ };
20622
+ const sparkClient = await this.connectionManager.createSparkClient(
20623
+ this.config.getCoordinatorAddress()
20624
+ );
20625
+ const response = await sparkClient.refresh_timelock({
20626
+ leafId: node.id,
20627
+ ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
20628
+ signingJobs: [refundSigningJob]
20629
+ });
20630
+ if (response.signingResults.length !== 1) {
20631
+ throw Error(
20632
+ `Expected 1 signing result, got ${response.signingResults.length}`
20633
+ );
20634
+ }
20635
+ const signingResult = response.signingResults[0];
20636
+ if (!signingResult || !refundSigningJob.signingNonceCommitment) {
20637
+ throw Error("Signing result or nonce commitment does not exist");
20638
+ }
20639
+ const rawTx = getTxFromRawTxBytes(refundSigningJob.rawTx);
20640
+ const txOut = nodeTx.getOutput(0);
20641
+ const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
20642
+ const userSignature = await this.config.signer.signFrost({
20643
+ message: rawTxSighash,
20644
+ privateAsPubKey: signingPubKey,
20645
+ publicKey: signingPubKey,
20646
+ verifyingKey: signingResult.verifyingKey,
20647
+ selfCommitment: refundSigningJob.signingNonceCommitment,
20648
+ statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
20649
+ adaptorPubKey: new Uint8Array()
20650
+ });
20651
+ const signature = await this.config.signer.aggregateFrost({
20652
+ message: rawTxSighash,
20653
+ statechainSignatures: signingResult.signingResult?.signatureShares,
20654
+ statechainPublicKeys: signingResult.signingResult?.publicKeys,
20655
+ verifyingKey: signingResult.verifyingKey,
20656
+ statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
20657
+ selfCommitment: refundSigningJob.signingNonceCommitment,
20658
+ publicKey: signingPubKey,
20659
+ selfSignature: userSignature,
20660
+ adaptorPubKey: new Uint8Array()
20661
+ });
20662
+ const result = await sparkClient.finalize_node_signatures({
20663
+ intent: 3 /* REFRESH */,
20664
+ nodeSignatures: [
20665
+ {
20666
+ nodeId: node.id,
20667
+ nodeTxSignature: new Uint8Array(),
20668
+ refundTxSignature: signature
20669
+ }
20670
+ ]
20671
+ });
20672
+ return result;
20673
+ }
20473
20674
  };
20474
20675
 
20475
20676
  // src/services/coop-exit.ts
@@ -20773,7 +20974,7 @@ var DepositService = class {
20773
20974
  depositTx,
20774
20975
  vout
20775
20976
  }) {
20776
- const rootTx = new import_btc_signer4.Transaction();
20977
+ const rootTx = new import_btc_signer4.Transaction({ version: 3 });
20777
20978
  const output = depositTx.getOutput(vout);
20778
20979
  if (!output) {
20779
20980
  throw new ValidationError("Invalid deposit transaction output", {
@@ -20791,17 +20992,19 @@ var DepositService = class {
20791
20992
  expected: "Output with script and amount"
20792
20993
  });
20793
20994
  }
20995
+ let outputAmount = amount;
20794
20996
  rootTx.addInput({
20795
20997
  txid: getTxId(depositTx),
20796
20998
  index: vout
20797
20999
  });
20798
21000
  rootTx.addOutput({
20799
21001
  script,
20800
- amount
21002
+ amount: outputAmount
20801
21003
  });
21004
+ rootTx.addOutput(getEphemeralAnchorOutput());
20802
21005
  const rootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
20803
21006
  const rootTxSighash = getSigHashFromTx(rootTx, 0, output);
20804
- const refundTx = new import_btc_signer4.Transaction();
21007
+ const refundTx = new import_btc_signer4.Transaction({ version: 3 });
20805
21008
  const sequence = 1 << 30 | INITIAL_TIME_LOCK2;
20806
21009
  refundTx.addInput({
20807
21010
  txid: getTxId(rootTx),
@@ -20816,10 +21019,11 @@ var DepositService = class {
20816
21019
  const refundPkScript = btc3.OutScript.encode(refundAddress);
20817
21020
  refundTx.addOutput({
20818
21021
  script: refundPkScript,
20819
- amount
21022
+ amount: outputAmount
20820
21023
  });
21024
+ refundTx.addOutput(getEphemeralAnchorOutput());
20821
21025
  const refundNonceCommitment = await this.config.signer.getRandomSigningCommitment();
20822
- const refundTxSighash = getSigHashFromTx(refundTx, 0, output);
21026
+ const refundTxSighash = getSigHashFromTx(refundTx, 0, rootTx.getOutput(0));
20823
21027
  const sparkClient = await this.connectionManager.createSparkClient(
20824
21028
  this.config.getCoordinatorAddress()
20825
21029
  );
@@ -25327,7 +25531,7 @@ function isValidPublicKey(publicKey) {
25327
25531
  }
25328
25532
 
25329
25533
  // src/services/token-transactions.ts
25330
- var MAX_TOKEN_OUTPUTS = 1e3;
25534
+ var MAX_TOKEN_OUTPUTS = 500;
25331
25535
  var TokenTransactionService = class {
25332
25536
  config;
25333
25537
  connectionManager;
@@ -25892,6 +26096,12 @@ var import_utils16 = require("@noble/curves/abstract/utils");
25892
26096
  var import_sha212 = require("@noble/hashes/sha2");
25893
26097
  var import_btc_signer5 = require("@scure/btc-signer");
25894
26098
  var INITIAL_TIME_LOCK3 = 2e3;
26099
+ function maybeApplyFee3(amount) {
26100
+ if (amount > BigInt(DEFAULT_FEE_SATS)) {
26101
+ return amount - BigInt(DEFAULT_FEE_SATS);
26102
+ }
26103
+ return amount;
26104
+ }
25895
26105
  var TreeCreationService = class {
25896
26106
  config;
25897
26107
  connectionManager;
@@ -26090,7 +26300,7 @@ var TreeCreationService = class {
26090
26300
  refundTxSigningJob: void 0,
26091
26301
  children: []
26092
26302
  };
26093
- const tx = new import_btc_signer5.Transaction();
26303
+ const tx = new import_btc_signer5.Transaction({ version: 3 });
26094
26304
  tx.addInput({
26095
26305
  txid: getTxId(parentTx),
26096
26306
  index: vout
@@ -26102,6 +26312,7 @@ var TreeCreationService = class {
26102
26312
  tx.addOutput({
26103
26313
  script: parentTxOut.script,
26104
26314
  amount: parentTxOut.amount
26315
+ // maybeApplyFee(parentTxOut.amount),
26105
26316
  });
26106
26317
  tx.addOutput(getEphemeralAnchorOutput());
26107
26318
  const signingNonceCommitment = await this.config.signer.getRandomSigningCommitment();
@@ -26118,7 +26329,7 @@ var TreeCreationService = class {
26118
26329
  refundTxSigningJob: void 0,
26119
26330
  children: []
26120
26331
  };
26121
- const childTx = new import_btc_signer5.Transaction();
26332
+ const childTx = new import_btc_signer5.Transaction({ version: 3 });
26122
26333
  childTx.addInput({
26123
26334
  txid: getTxId(tx),
26124
26335
  index: 0,
@@ -26127,6 +26338,7 @@ var TreeCreationService = class {
26127
26338
  childTx.addOutput({
26128
26339
  script: parentTxOut.script,
26129
26340
  amount: parentTxOut.amount
26341
+ // maybeApplyFee(parentTxOut.amount),
26130
26342
  });
26131
26343
  childTx.addOutput(getEphemeralAnchorOutput());
26132
26344
  const childSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
@@ -26137,7 +26349,7 @@ var TreeCreationService = class {
26137
26349
  };
26138
26350
  childCreationNode.nodeTxSigningCommitment = childSigningNonceCommitment;
26139
26351
  childCreationNode.nodeTxSigningJob = childSigningJob;
26140
- const refundTx = new import_btc_signer5.Transaction();
26352
+ const refundTx = new import_btc_signer5.Transaction({ version: 3 });
26141
26353
  refundTx.addInput({
26142
26354
  txid: getTxId(childTx),
26143
26355
  index: 0,
@@ -26153,8 +26365,9 @@ var TreeCreationService = class {
26153
26365
  const refundPkScript = import_btc_signer5.OutScript.encode(refundAddress);
26154
26366
  refundTx.addOutput({
26155
26367
  script: refundPkScript,
26156
- amount: parentTxOut.amount
26368
+ amount: maybeApplyFee3(parentTxOut.amount)
26157
26369
  });
26370
+ refundTx.addOutput(getEphemeralAnchorOutput());
26158
26371
  const refundSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
26159
26372
  const refundSigningJob = {
26160
26373
  signingPublicKey: node.signingPublicKey,
@@ -26171,7 +26384,7 @@ var TreeCreationService = class {
26171
26384
  if (!parentTxOutput?.script || !parentTxOutput?.amount) {
26172
26385
  throw new Error("parentTxOutput is undefined");
26173
26386
  }
26174
- const rootNodeTx = new import_btc_signer5.Transaction();
26387
+ const rootNodeTx = new import_btc_signer5.Transaction({ version: 3 });
26175
26388
  rootNodeTx.addInput({
26176
26389
  txid: getTxId(parentTx),
26177
26390
  index: vout
@@ -26186,10 +26399,10 @@ var TreeCreationService = class {
26186
26399
  rootNodeTx.addOutput({
26187
26400
  script: childPkScript,
26188
26401
  amount: parentTxOutput.amount / 2n
26402
+ // feeAdjustedAmount / 2n,
26189
26403
  });
26190
26404
  }
26191
- const anchor = getEphemeralAnchorOutput();
26192
- rootNodeTx.addOutput(anchor);
26405
+ rootNodeTx.addOutput(getEphemeralAnchorOutput());
26193
26406
  const rootNodeSigningCommitment = await this.config.signer.getRandomSigningCommitment();
26194
26407
  const rootNodeSigningJob = {
26195
26408
  signingPublicKey: root.signingPublicKey,
@@ -26340,7 +26553,7 @@ var TreeCreationService = class {
26340
26553
  };
26341
26554
 
26342
26555
  // src/spark-wallet/spark-wallet.ts
26343
- var import_lrc20_sdk5 = require("@buildonspark/lrc20-sdk");
26556
+ var import_lrc20_sdk3 = require("@buildonspark/lrc20-sdk");
26344
26557
  var import_sha213 = require("@noble/hashes/sha2");
26345
26558
  var import_eventemitter3 = require("eventemitter3");
26346
26559
 
@@ -27249,7 +27462,7 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
27249
27462
  }
27250
27463
  await this.initWalletFromSeed(seed, accountNumber);
27251
27464
  const network = this.config.getNetwork();
27252
- this.lrc20Wallet = await import_lrc20_sdk5.LRCWallet.create(
27465
+ this.lrc20Wallet = await import_lrc20_sdk3.LRCWallet.create(
27253
27466
  LRC_WALLET_NETWORK[network],
27254
27467
  LRC_WALLET_NETWORK_TYPE[network],
27255
27468
  this.config.lrc20ApiConfig,
@@ -27448,7 +27661,7 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
27448
27661
  adaptorPrivateKey
27449
27662
  );
27450
27663
  }
27451
- await this.transferService.sendTransferTweakKey(
27664
+ await this.transferService.deliverTransferPackage(
27452
27665
  transfer,
27453
27666
  leafKeyTweaks,
27454
27667
  signatureMap
@@ -29133,6 +29346,112 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
29133
29346
  }
29134
29347
  return this.config.signer.validateMessageWithIdentityKey(hash, signature);
29135
29348
  }
29349
+ /**
29350
+ * Signs a transaction with wallet keys.
29351
+ *
29352
+ * @param {string} txHex - The transaction hex to sign
29353
+ * @param {string} keyType - The type of key to use for signing ("identity", "deposit", or "auto-detect")
29354
+ * @returns {Promise<string>} The signed transaction hex
29355
+ */
29356
+ async signTransaction(txHex, keyType = "auto-detect") {
29357
+ try {
29358
+ const tx = import_btc_signer7.Transaction.fromRaw((0, import_utils21.hexToBytes)(txHex));
29359
+ let publicKey;
29360
+ switch (keyType.toLowerCase()) {
29361
+ case "identity":
29362
+ publicKey = await this.config.signer.getIdentityPublicKey();
29363
+ break;
29364
+ case "deposit":
29365
+ publicKey = await this.config.signer.getDepositSigningKey();
29366
+ break;
29367
+ case "auto-detect":
29368
+ default:
29369
+ const detectedKey = await this.detectKeyForTransaction(tx);
29370
+ if (detectedKey) {
29371
+ publicKey = detectedKey.publicKey;
29372
+ } else {
29373
+ publicKey = await this.config.signer.getIdentityPublicKey();
29374
+ }
29375
+ break;
29376
+ }
29377
+ let inputsSigned = 0;
29378
+ for (let i = 0; i < tx.inputsLength; i++) {
29379
+ const input = tx.getInput(i);
29380
+ if (!input?.witnessUtxo?.script) {
29381
+ continue;
29382
+ }
29383
+ const script = input.witnessUtxo.script;
29384
+ if (script.length === 1 && script[0] === 81) {
29385
+ continue;
29386
+ }
29387
+ const identityScript = getP2TRScriptFromPublicKey(
29388
+ publicKey,
29389
+ this.config.getNetwork()
29390
+ );
29391
+ if ((0, import_utils21.bytesToHex)(script) === (0, import_utils21.bytesToHex)(identityScript)) {
29392
+ try {
29393
+ this.config.signer.signTransactionIndex(tx, i, publicKey);
29394
+ inputsSigned++;
29395
+ } catch (error) {
29396
+ throw new ValidationError(`Failed to sign input ${i}: ${error}`, {
29397
+ field: "input",
29398
+ value: i
29399
+ });
29400
+ }
29401
+ }
29402
+ }
29403
+ if (inputsSigned === 0) {
29404
+ throw new Error(
29405
+ "No inputs were signed. Check that the transaction contains inputs controlled by this wallet."
29406
+ );
29407
+ }
29408
+ tx.finalize();
29409
+ const signedTxHex = tx.hex;
29410
+ return signedTxHex;
29411
+ } catch (error) {
29412
+ console.error("\u274C Error signing transaction:", error);
29413
+ throw error;
29414
+ }
29415
+ }
29416
+ /**
29417
+ * Helper method to auto-detect which key should be used for signing a transaction.
29418
+ */
29419
+ async detectKeyForTransaction(tx) {
29420
+ try {
29421
+ const identityPubKey = await this.config.signer.getIdentityPublicKey();
29422
+ const depositPubKey = await this.config.signer.getDepositSigningKey();
29423
+ for (let i = 0; i < tx.inputsLength; i++) {
29424
+ const input = tx.getInput(i);
29425
+ if (input?.witnessUtxo?.script) {
29426
+ const script = input.witnessUtxo.script;
29427
+ const identityScript = getP2TRScriptFromPublicKey(
29428
+ identityPubKey,
29429
+ this.config.getNetwork()
29430
+ );
29431
+ const depositScript = getP2TRScriptFromPublicKey(
29432
+ depositPubKey,
29433
+ this.config.getNetwork()
29434
+ );
29435
+ if ((0, import_utils21.bytesToHex)(script) === (0, import_utils21.bytesToHex)(identityScript)) {
29436
+ return {
29437
+ publicKey: identityPubKey,
29438
+ keyType: "identity"
29439
+ };
29440
+ }
29441
+ if ((0, import_utils21.bytesToHex)(script) === (0, import_utils21.bytesToHex)(depositScript)) {
29442
+ return {
29443
+ publicKey: depositPubKey,
29444
+ keyType: "deposit"
29445
+ };
29446
+ }
29447
+ }
29448
+ }
29449
+ return null;
29450
+ } catch (error) {
29451
+ console.warn("Error during key auto-detection:", error);
29452
+ return null;
29453
+ }
29454
+ }
29136
29455
  /**
29137
29456
  * Get a Lightning receive request by ID.
29138
29457
  *
@@ -29163,6 +29482,246 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
29163
29482
  const sspClient = this.getSspClient();
29164
29483
  return await sspClient.getCoopExitRequest(id);
29165
29484
  }
29485
+ /**
29486
+ * Check the remaining timelock on a given node.
29487
+ *
29488
+ * @param {string} nodeId - The ID of the node to check
29489
+ * @returns {Promise<{nodeTimelock: number, refundTimelock: number}>} The remaining timelocks in blocks for both node and refund transactions
29490
+ */
29491
+ async checkTimelock(nodeId) {
29492
+ const sparkClient = await this.connectionManager.createSparkClient(
29493
+ this.config.getCoordinatorAddress()
29494
+ );
29495
+ try {
29496
+ const response = await sparkClient.query_nodes({
29497
+ source: {
29498
+ $case: "nodeIds",
29499
+ nodeIds: {
29500
+ nodeIds: [nodeId]
29501
+ }
29502
+ },
29503
+ includeParents: false,
29504
+ network: NetworkToProto[this.config.getNetwork()]
29505
+ });
29506
+ const node = response.nodes[nodeId];
29507
+ if (!node) {
29508
+ throw new ValidationError("Node not found", {
29509
+ field: "nodeId",
29510
+ value: nodeId
29511
+ });
29512
+ }
29513
+ const isRootNode = !node.parentNodeId;
29514
+ if (!node.nodeTx || node.nodeTx.length === 0) {
29515
+ throw new ValidationError(
29516
+ `Node transaction data is missing or empty for ${isRootNode ? "root" : "non-root"} node`,
29517
+ {
29518
+ field: "nodeTx",
29519
+ value: node.nodeTx?.length || 0
29520
+ }
29521
+ );
29522
+ }
29523
+ if (!node.refundTx || node.refundTx.length === 0) {
29524
+ throw new ValidationError(
29525
+ `Refund transaction data is missing or empty for ${isRootNode ? "root" : "non-root"} node`,
29526
+ {
29527
+ field: "refundTx",
29528
+ value: node.refundTx?.length || 0
29529
+ }
29530
+ );
29531
+ }
29532
+ let nodeTx, refundTx;
29533
+ try {
29534
+ nodeTx = getTxFromRawTxBytes(node.nodeTx);
29535
+ } catch (error) {
29536
+ throw new ValidationError(
29537
+ `Failed to parse node transaction for ${isRootNode ? "root" : "non-root"} node: ${error instanceof Error ? error.message : String(error)}`,
29538
+ {
29539
+ field: "nodeTx",
29540
+ value: node.nodeTx.length
29541
+ }
29542
+ );
29543
+ }
29544
+ try {
29545
+ refundTx = getTxFromRawTxBytes(node.refundTx);
29546
+ } catch (error) {
29547
+ throw new ValidationError(
29548
+ `Failed to parse refund transaction for ${isRootNode ? "root" : "non-root"} node: ${error instanceof Error ? error.message : String(error)}`,
29549
+ {
29550
+ field: "refundTx",
29551
+ value: node.refundTx.length
29552
+ }
29553
+ );
29554
+ }
29555
+ const nodeInput = nodeTx.getInput(0);
29556
+ if (!nodeInput) {
29557
+ throw new ValidationError(
29558
+ `Node transaction has no inputs for ${isRootNode ? "root" : "non-root"} node`,
29559
+ {
29560
+ field: "nodeInput",
29561
+ value: nodeTx.inputsLength
29562
+ }
29563
+ );
29564
+ }
29565
+ if (!nodeInput.sequence) {
29566
+ throw new ValidationError(
29567
+ `Node transaction has no sequence for ${isRootNode ? "root" : "non-root"} node`,
29568
+ {
29569
+ field: "sequence",
29570
+ value: nodeInput.sequence
29571
+ }
29572
+ );
29573
+ }
29574
+ const refundInput = refundTx.getInput(0);
29575
+ if (!refundInput) {
29576
+ throw new ValidationError(
29577
+ `Refund transaction has no inputs for ${isRootNode ? "root" : "non-root"} node`,
29578
+ {
29579
+ field: "refundInput",
29580
+ value: refundTx.inputsLength
29581
+ }
29582
+ );
29583
+ }
29584
+ if (!refundInput.sequence) {
29585
+ throw new ValidationError(
29586
+ `Refund transaction has no sequence for ${isRootNode ? "root" : "non-root"} node`,
29587
+ {
29588
+ field: "sequence",
29589
+ value: refundInput.sequence
29590
+ }
29591
+ );
29592
+ }
29593
+ const nodeTimelock = nodeInput.sequence & 65535;
29594
+ const refundTimelock = refundInput.sequence & 65535;
29595
+ return {
29596
+ nodeTimelock,
29597
+ refundTimelock
29598
+ };
29599
+ } catch (error) {
29600
+ throw new NetworkError(
29601
+ `Failed to check timelock for node ${nodeId}`,
29602
+ {
29603
+ method: "query_nodes"
29604
+ },
29605
+ error
29606
+ );
29607
+ }
29608
+ }
29609
+ /**
29610
+ * Refresh the timelock of a specific node.
29611
+ *
29612
+ * @param {string} nodeId - The ID of the node to refresh
29613
+ * @returns {Promise<void>} Promise that resolves when the timelock is refreshed
29614
+ */
29615
+ async testOnly_expireTimelock(nodeId) {
29616
+ const sparkClient = await this.connectionManager.createSparkClient(
29617
+ this.config.getCoordinatorAddress()
29618
+ );
29619
+ try {
29620
+ const response = await sparkClient.query_nodes({
29621
+ source: {
29622
+ $case: "nodeIds",
29623
+ nodeIds: {
29624
+ nodeIds: [nodeId]
29625
+ }
29626
+ },
29627
+ includeParents: true
29628
+ });
29629
+ const node = response.nodes[nodeId];
29630
+ if (!node) {
29631
+ throw new ValidationError("Node not found", {
29632
+ field: "nodeId",
29633
+ value: nodeId
29634
+ });
29635
+ }
29636
+ if (!node.parentNodeId) {
29637
+ throw new ValidationError("Node has no parent", {
29638
+ field: "parentNodeId",
29639
+ value: node.parentNodeId
29640
+ });
29641
+ }
29642
+ const parentNode = response.nodes[node.parentNodeId];
29643
+ if (!parentNode) {
29644
+ throw new ValidationError("Parent node not found", {
29645
+ field: "parentNodeId",
29646
+ value: node.parentNodeId
29647
+ });
29648
+ }
29649
+ const signingPubKey = await this.config.signer.generatePublicKey(
29650
+ (0, import_sha213.sha256)(node.id)
29651
+ );
29652
+ const result = await this.transferService.refreshTimelockNodes(
29653
+ [node],
29654
+ parentNode,
29655
+ signingPubKey
29656
+ );
29657
+ const leafIndex = this.leaves.findIndex((leaf) => leaf.id === node.id);
29658
+ if (leafIndex !== -1 && result.nodes.length > 0) {
29659
+ const newNode = result.nodes[0];
29660
+ if (newNode) {
29661
+ this.leaves[leafIndex] = newNode;
29662
+ }
29663
+ }
29664
+ } catch (error) {
29665
+ throw new NetworkError(
29666
+ "Failed to refresh timelock",
29667
+ {
29668
+ method: "refresh_timelock"
29669
+ },
29670
+ error
29671
+ );
29672
+ }
29673
+ }
29674
+ /**
29675
+ * Refresh the timelock of a specific node's refund transaction only.
29676
+ *
29677
+ * @param {string} nodeId - The ID of the node whose refund transaction to refresh
29678
+ * @returns {Promise<void>} Promise that resolves when the refund timelock is refreshed
29679
+ */
29680
+ async testOnly_expireTimelockRefundTx(nodeId) {
29681
+ const sparkClient = await this.connectionManager.createSparkClient(
29682
+ this.config.getCoordinatorAddress()
29683
+ );
29684
+ try {
29685
+ const response = await sparkClient.query_nodes({
29686
+ source: {
29687
+ $case: "nodeIds",
29688
+ nodeIds: {
29689
+ nodeIds: [nodeId]
29690
+ }
29691
+ },
29692
+ includeParents: false
29693
+ });
29694
+ const node = response.nodes[nodeId];
29695
+ if (!node) {
29696
+ throw new ValidationError("Node not found", {
29697
+ field: "nodeId",
29698
+ value: nodeId
29699
+ });
29700
+ }
29701
+ const signingPubKey = await this.config.signer.generatePublicKey(
29702
+ (0, import_sha213.sha256)(node.id)
29703
+ );
29704
+ const result = await this.transferService.refreshTimelockRefundTx(
29705
+ node,
29706
+ signingPubKey
29707
+ );
29708
+ const leafIndex = this.leaves.findIndex((leaf) => leaf.id === node.id);
29709
+ if (leafIndex !== -1 && result.nodes.length > 0) {
29710
+ const newNode = result.nodes[0];
29711
+ if (newNode) {
29712
+ this.leaves[leafIndex] = newNode;
29713
+ }
29714
+ }
29715
+ } catch (error) {
29716
+ throw new NetworkError(
29717
+ "Failed to refresh refund timelock",
29718
+ {
29719
+ method: "refresh_timelock_refund_tx"
29720
+ },
29721
+ error
29722
+ );
29723
+ }
29724
+ }
29166
29725
  cleanup() {
29167
29726
  if (this.claimTransfersInterval) {
29168
29727
  clearInterval(this.claimTransfersInterval);
@@ -29251,13 +29810,476 @@ async function getLatestDepositTxId(address2) {
29251
29810
  }
29252
29811
  return null;
29253
29812
  }
29813
+ async function isTxBroadcast(txid, baseUrl, network) {
29814
+ const headers = {};
29815
+ if (network === 3 /* REGTEST */) {
29816
+ const auth = btoa(
29817
+ `${ELECTRS_CREDENTIALS.username}:${ELECTRS_CREDENTIALS.password}`
29818
+ );
29819
+ headers["Authorization"] = `Basic ${auth}`;
29820
+ }
29821
+ const response = await fetch(`${baseUrl}/tx/${txid}`, {
29822
+ headers
29823
+ });
29824
+ const tx = await response.json();
29825
+ if (tx.error) {
29826
+ return false;
29827
+ }
29828
+ return true;
29829
+ }
29254
29830
 
29255
29831
  // src/utils/index.ts
29256
29832
  init_buffer();
29833
+
29834
+ // src/utils/unilateral-exit.ts
29835
+ init_buffer();
29836
+ var import_utils22 = require("@noble/curves/abstract/utils");
29837
+ var import_legacy = require("@noble/hashes/legacy");
29838
+ var import_sha214 = require("@noble/hashes/sha2");
29839
+ var btc5 = __toESM(require("@scure/btc-signer"), 1);
29840
+ function isEphemeralAnchorOutput(script, amount) {
29841
+ return Boolean(
29842
+ amount === 0n && script && // Pattern 1: Bare OP_TRUE (single byte 0x51)
29843
+ (script.length === 1 && script[0] === 81 || // Pattern 2: Push OP_TRUE (two bytes 0x01 0x51) - MALFORMED but we detect it
29844
+ script.length === 2 && script[0] === 1 && script[1] === 81 || // Pattern 3: Bitcoin v29 ephemeral anchor script (7 bytes: 015152014e0173)
29845
+ script.length === 7 && script[0] === 1 && script[1] === 81 && script[2] === 82 && script[3] === 1 && script[4] === 78 && script[5] === 1 && script[6] === 115 || // Pattern 4: Bitcoin ephemeral anchor OP_1 + push 2 bytes (4 bytes: 51024e73)
29846
+ script.length === 4 && script[0] === 81 && script[1] === 2 && script[2] === 78 && script[3] === 115)
29847
+ );
29848
+ }
29849
+ async function constructUnilateralExitTxs(nodeHexStrings, sparkClient, network) {
29850
+ const result = [];
29851
+ const nodes = nodeHexStrings.map((hex) => TreeNode.decode((0, import_utils22.hexToBytes)(hex)));
29852
+ const nodeMap = /* @__PURE__ */ new Map();
29853
+ for (const node of nodes) {
29854
+ nodeMap.set(node.id, node);
29855
+ }
29856
+ for (const node of nodes) {
29857
+ const transactions = [];
29858
+ const chain = [];
29859
+ let currentNode = node;
29860
+ while (currentNode) {
29861
+ chain.unshift(currentNode);
29862
+ if (currentNode.parentNodeId) {
29863
+ let parentNode = nodeMap.get(currentNode.parentNodeId);
29864
+ if (!parentNode && sparkClient) {
29865
+ try {
29866
+ const response = await sparkClient.query_nodes({
29867
+ source: {
29868
+ $case: "nodeIds",
29869
+ nodeIds: {
29870
+ nodeIds: [currentNode.parentNodeId]
29871
+ }
29872
+ },
29873
+ includeParents: true,
29874
+ network: network || 0
29875
+ // Default to mainnet if not provided
29876
+ });
29877
+ parentNode = response.nodes[currentNode.parentNodeId];
29878
+ if (parentNode) {
29879
+ nodeMap.set(currentNode.parentNodeId, parentNode);
29880
+ }
29881
+ } catch (error) {
29882
+ console.warn(
29883
+ `Failed to query parent node ${currentNode.parentNodeId}: ${error}`
29884
+ );
29885
+ break;
29886
+ }
29887
+ }
29888
+ if (parentNode) {
29889
+ currentNode = parentNode;
29890
+ } else {
29891
+ if (!sparkClient) {
29892
+ console.warn(
29893
+ `Parent node ${currentNode.parentNodeId} not found. Provide a sparkClient to fetch missing parents.`
29894
+ );
29895
+ } else {
29896
+ console.warn(
29897
+ `Parent node ${currentNode.parentNodeId} not found in database. Chain may be incomplete.`
29898
+ );
29899
+ }
29900
+ break;
29901
+ }
29902
+ } else {
29903
+ break;
29904
+ }
29905
+ }
29906
+ for (const chainNode of chain) {
29907
+ const nodeTx = (0, import_utils22.bytesToHex)(chainNode.nodeTx);
29908
+ transactions.push(nodeTx);
29909
+ if (chainNode.id === node.id) {
29910
+ const refundTx = (0, import_utils22.bytesToHex)(chainNode.refundTx);
29911
+ transactions.push(refundTx);
29912
+ }
29913
+ }
29914
+ result.push({
29915
+ leafId: node.id,
29916
+ transactions
29917
+ });
29918
+ }
29919
+ return result;
29920
+ }
29921
+ async function constructUnilateralExitFeeBumpPackages(nodeHexStrings, utxos, feeRate, electrsUrl, sparkClient, network) {
29922
+ const result = [];
29923
+ const availableUtxos = [...utxos].sort((a, b) => {
29924
+ if (a.value > b.value) return -1;
29925
+ if (a.value < b.value) return 1;
29926
+ return 0;
29927
+ });
29928
+ const nodes = [];
29929
+ for (let i = 0; i < nodeHexStrings.length; i++) {
29930
+ const hex = nodeHexStrings[i];
29931
+ try {
29932
+ if (!hex || hex.length === 0) {
29933
+ throw new Error(`Node hex string at index ${i} is empty`);
29934
+ }
29935
+ if (hex.startsWith("03000000") || hex.startsWith("02000000") || hex.startsWith("01000000")) {
29936
+ throw new Error(
29937
+ `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.`
29938
+ );
29939
+ }
29940
+ const nodeBytes = (0, import_utils22.hexToBytes)(hex);
29941
+ const node = TreeNode.decode(nodeBytes);
29942
+ if (!node.id) {
29943
+ throw new Error(
29944
+ `Decoded TreeNode at index ${i} is missing required 'id' field`
29945
+ );
29946
+ }
29947
+ if (!node.nodeTx || node.nodeTx.length === 0) {
29948
+ throw new Error(
29949
+ `Decoded TreeNode at index ${i} is missing required 'nodeTx' field`
29950
+ );
29951
+ }
29952
+ nodes.push(node);
29953
+ } catch (decodeError) {
29954
+ throw new Error(
29955
+ `Failed to decode TreeNode hex string at index ${i}: ${decodeError}. Make sure you're providing TreeNode protobuf hex strings, not raw transaction hex. Use 'leafidtohex' command to get proper hex strings.`
29956
+ );
29957
+ }
29958
+ }
29959
+ const nodeMap = /* @__PURE__ */ new Map();
29960
+ for (const node of nodes) {
29961
+ nodeMap.set(node.id, node);
29962
+ }
29963
+ const broadcastTxs = /* @__PURE__ */ new Map();
29964
+ for (const node of nodes) {
29965
+ const txPackages = [];
29966
+ let previousFeeBumpTx;
29967
+ const chain = [];
29968
+ let currentNode = node;
29969
+ while (currentNode) {
29970
+ chain.unshift(currentNode);
29971
+ if (currentNode.parentNodeId) {
29972
+ let parentNode = nodeMap.get(currentNode.parentNodeId);
29973
+ if (!parentNode && sparkClient) {
29974
+ try {
29975
+ const response = await sparkClient.query_nodes({
29976
+ source: {
29977
+ $case: "nodeIds",
29978
+ nodeIds: {
29979
+ nodeIds: [currentNode.parentNodeId]
29980
+ }
29981
+ },
29982
+ includeParents: true,
29983
+ network: network || 0
29984
+ // Default to mainnet if not provided
29985
+ });
29986
+ parentNode = response.nodes[currentNode.parentNodeId];
29987
+ if (parentNode) {
29988
+ nodeMap.set(currentNode.parentNodeId, parentNode);
29989
+ }
29990
+ } catch (error) {
29991
+ console.warn(
29992
+ `Failed to query parent node ${currentNode.parentNodeId}: ${error}`
29993
+ );
29994
+ break;
29995
+ }
29996
+ }
29997
+ if (parentNode) {
29998
+ currentNode = parentNode;
29999
+ } else {
30000
+ if (!sparkClient) {
30001
+ console.warn(
30002
+ `Parent node ${currentNode.parentNodeId} not found. Provide a sparkClient to fetch missing parents.`
30003
+ );
30004
+ } else {
30005
+ console.warn(
30006
+ `Parent node ${currentNode.parentNodeId} not found in database. Chain may be incomplete.`
30007
+ );
30008
+ }
30009
+ break;
30010
+ }
30011
+ } else {
30012
+ break;
30013
+ }
30014
+ }
30015
+ for (const chainNode of chain) {
30016
+ let nodeTxHex = (0, import_utils22.bytesToHex)(chainNode.nodeTx);
30017
+ try {
30018
+ const txObj = getTxFromRawTxHex(nodeTxHex);
30019
+ const txid = getTxId(txObj);
30020
+ if (broadcastTxs.get(txid)) {
30021
+ continue;
30022
+ }
30023
+ broadcastTxs.set(txid, true);
30024
+ const isBroadcast = await isTxBroadcast(txid, electrsUrl, network);
30025
+ if (isBroadcast) {
30026
+ continue;
30027
+ } else {
30028
+ }
30029
+ let anchorOutputScriptHex;
30030
+ for (let i = txObj.outputsLength - 1; i >= 0; i--) {
30031
+ const output = txObj.getOutput(i);
30032
+ if (output?.amount === 0n && output.script) {
30033
+ anchorOutputScriptHex = (0, import_utils22.bytesToHex)(output.script);
30034
+ break;
30035
+ }
30036
+ }
30037
+ } catch (parseError) {
30038
+ console.error(
30039
+ `\u274C Error parsing nodeTx for anchor check (node ${chainNode.id}): ${parseError}`
30040
+ );
30041
+ console.log(
30042
+ ` This may indicate a corrupted transaction in the TreeNode.`
30043
+ );
30044
+ console.log(` Transaction hex: ${nodeTxHex}`);
30045
+ console.log(
30046
+ ` Attempting to continue with original hex, but fee bump may fail.`
30047
+ );
30048
+ }
30049
+ const {
30050
+ feeBumpPsbt: nodeFeeBumpPsbt,
30051
+ usedUtxos,
30052
+ correctedParentTx
30053
+ } = constructFeeBumpTx(nodeTxHex, availableUtxos, feeRate, void 0);
30054
+ const feeBumpTx = btc5.Transaction.fromPSBT((0, import_utils22.hexToBytes)(nodeFeeBumpPsbt));
30055
+ var feeBumpOut = feeBumpTx.outputsLength === 1 ? feeBumpTx.getOutput(0) : null;
30056
+ var feeBumpOutPubKey = null;
30057
+ for (const usedUtxo of usedUtxos) {
30058
+ if (feeBumpOut && (0, import_utils22.bytesToHex)(feeBumpOut.script) == usedUtxo.script) {
30059
+ feeBumpOutPubKey = usedUtxo.publicKey;
30060
+ }
30061
+ const index = availableUtxos.findIndex(
30062
+ (u) => u.txid === usedUtxo.txid && u.vout === usedUtxo.vout
30063
+ );
30064
+ if (index !== -1) {
30065
+ availableUtxos.splice(index, 1);
30066
+ }
30067
+ }
30068
+ if (feeBumpOut)
30069
+ availableUtxos.unshift({
30070
+ txid: getTxId(feeBumpTx),
30071
+ vout: 0,
30072
+ value: feeBumpOut.amount,
30073
+ script: (0, import_utils22.bytesToHex)(feeBumpOut.script),
30074
+ publicKey: feeBumpOutPubKey
30075
+ });
30076
+ const finalNodeTx = correctedParentTx || nodeTxHex;
30077
+ txPackages.push({ tx: finalNodeTx, feeBumpPsbt: nodeFeeBumpPsbt });
30078
+ if (chainNode.id === node.id) {
30079
+ let refundTxHex = (0, import_utils22.bytesToHex)(chainNode.refundTx);
30080
+ try {
30081
+ const txObj = getTxFromRawTxHex(refundTxHex);
30082
+ let anchorOutputScriptHex;
30083
+ for (let i = txObj.outputsLength - 1; i >= 0; i--) {
30084
+ const output = txObj.getOutput(i);
30085
+ if (output?.amount === 0n && output.script) {
30086
+ anchorOutputScriptHex = (0, import_utils22.bytesToHex)(output.script);
30087
+ break;
30088
+ }
30089
+ }
30090
+ } catch (parseError) {
30091
+ console.error(
30092
+ `\u274C Error parsing refundTx for anchor check (node ${chainNode.id}): ${parseError}`
30093
+ );
30094
+ console.log(
30095
+ ` This may indicate a corrupted refund transaction in the TreeNode.`
30096
+ );
30097
+ console.log(` Refund transaction hex: ${refundTxHex}`);
30098
+ console.log(
30099
+ ` Attempting to continue with original refund hex, but this transaction may be invalid.`
30100
+ );
30101
+ }
30102
+ const refundFeeBump = constructFeeBumpTx(
30103
+ refundTxHex,
30104
+ availableUtxos,
30105
+ feeRate,
30106
+ void 0
30107
+ );
30108
+ txPackages.push({
30109
+ tx: refundTxHex,
30110
+ feeBumpPsbt: refundFeeBump.feeBumpPsbt
30111
+ });
30112
+ }
30113
+ }
30114
+ result.push({
30115
+ leafId: node.id,
30116
+ txPackages
30117
+ });
30118
+ }
30119
+ return result;
30120
+ }
30121
+ function hash160(data) {
30122
+ const sha256Hash = (0, import_sha214.sha256)(data);
30123
+ return (0, import_legacy.ripemd160)(sha256Hash);
30124
+ }
30125
+ function constructFeeBumpTx(txHex, utxos, feeRate, previousFeeBumpTx) {
30126
+ if (!txHex || txHex.length === 0) {
30127
+ throw new Error("Transaction hex string is empty or undefined");
30128
+ }
30129
+ if (utxos.length === 0) {
30130
+ throw new Error("No UTXOs available for fee bump");
30131
+ }
30132
+ let correctedTxHex = txHex;
30133
+ let parentTx;
30134
+ try {
30135
+ parentTx = getTxFromRawTxHex(correctedTxHex);
30136
+ if (!parentTx) {
30137
+ throw new Error("getTxFromRawTxHex returned null/undefined");
30138
+ }
30139
+ } catch (parseError) {
30140
+ throw new Error(
30141
+ `Failed to parse parent transaction hex: ${parseError}. Transaction hex: ${correctedTxHex}`
30142
+ );
30143
+ }
30144
+ try {
30145
+ const outputsLength = parentTx.outputsLength;
30146
+ const inputsLength = parentTx.inputsLength;
30147
+ if (typeof outputsLength !== "number" || outputsLength < 0) {
30148
+ throw new Error(
30149
+ "Invalid transaction: outputsLength is not a valid number"
30150
+ );
30151
+ }
30152
+ if (typeof inputsLength !== "number" || inputsLength < 0) {
30153
+ throw new Error(
30154
+ "Invalid transaction: inputsLength is not a valid number"
30155
+ );
30156
+ }
30157
+ } catch (validationError) {
30158
+ throw new Error(
30159
+ `Transaction validation failed: ${validationError}. This may indicate a corrupted or malformed transaction.`
30160
+ );
30161
+ }
30162
+ const parentTxIdFromLib = parentTx.id;
30163
+ let ephemeralAnchorIndex = -1;
30164
+ for (let i = 0; i < parentTx.outputsLength; i++) {
30165
+ const output = parentTx.getOutput(i);
30166
+ const isEphemeralAnchor = isEphemeralAnchorOutput(
30167
+ output?.script,
30168
+ output?.amount
30169
+ );
30170
+ if (isEphemeralAnchor) {
30171
+ ephemeralAnchorIndex = i;
30172
+ break;
30173
+ }
30174
+ }
30175
+ if (ephemeralAnchorIndex === -1) {
30176
+ throw new Error(
30177
+ "No ephemeral anchor output found in parent transaction. Expected a 0-value output with OP_TRUE script (0x51), malformed OP_TRUE (0x0151), Bitcoin v29 ephemeral anchor script (015152014e0173), or Bitcoin OP_1 + push 2 bytes script (51024e73)."
30178
+ );
30179
+ }
30180
+ const ephemeralAnchorOutput = parentTx.getOutput(ephemeralAnchorIndex);
30181
+ if (!ephemeralAnchorOutput)
30182
+ throw new Error("No ephemeral anchor output found");
30183
+ if (!ephemeralAnchorOutput.script)
30184
+ throw new Error("No script found in ephemeral anchor output");
30185
+ if (utxos.length === 0) {
30186
+ throw new Error("No UTXOs available for fee bump");
30187
+ }
30188
+ const builder = new btc5.Transaction({
30189
+ version: 3,
30190
+ allowUnknown: true,
30191
+ allowLegacyWitnessUtxo: true
30192
+ });
30193
+ let totalValue = 0n;
30194
+ const processedUtxos = [];
30195
+ for (let i = 0; i < utxos.length; i++) {
30196
+ const fundingUtxo = utxos[i];
30197
+ if (!fundingUtxo) {
30198
+ throw new Error(`UTXO at index ${i} is undefined`);
30199
+ }
30200
+ const pubKeyHash = hash160((0, import_utils22.hexToBytes)(fundingUtxo.publicKey));
30201
+ const scriptToUse = new Uint8Array([0, 20, ...pubKeyHash]);
30202
+ const providedScript = (0, import_utils22.hexToBytes)(fundingUtxo.script);
30203
+ if ((0, import_utils22.bytesToHex)(scriptToUse) !== (0, import_utils22.bytesToHex)(providedScript)) {
30204
+ throw new Error(
30205
+ `\u274C Derived script doesn't match provided script for UTXO ${i + 1}.`
30206
+ );
30207
+ }
30208
+ builder.addInput({
30209
+ txid: fundingUtxo.txid,
30210
+ index: fundingUtxo.vout,
30211
+ sequence: 4294967295,
30212
+ witnessUtxo: {
30213
+ script: scriptToUse,
30214
+ // Always P2WPKH
30215
+ amount: fundingUtxo.value
30216
+ }
30217
+ });
30218
+ totalValue += fundingUtxo.value;
30219
+ processedUtxos.push({
30220
+ utxo: fundingUtxo,
30221
+ p2wpkhScript: scriptToUse
30222
+ });
30223
+ }
30224
+ builder.addInput({
30225
+ txid: parentTxIdFromLib,
30226
+ index: ephemeralAnchorIndex,
30227
+ sequence: 4294967295,
30228
+ witnessUtxo: {
30229
+ script: ephemeralAnchorOutput.script,
30230
+ // Use the original script directly (not P2WSH wrapped)
30231
+ amount: 0n
30232
+ }
30233
+ });
30234
+ const fee = 1500n;
30235
+ const remainingValue = totalValue - fee;
30236
+ if (remainingValue <= 0n) {
30237
+ throw new Error(
30238
+ `Insufficient funds for fee bump. Required fee: ${fee} sats, Available: ${totalValue} sats`
30239
+ );
30240
+ }
30241
+ if (processedUtxos.length === 0) {
30242
+ throw new Error("No processed UTXOs available for change output");
30243
+ }
30244
+ const firstProcessedUtxo = processedUtxos[0];
30245
+ if (!firstProcessedUtxo) {
30246
+ throw new Error("First processed UTXO is undefined");
30247
+ }
30248
+ builder.addOutput({
30249
+ script: firstProcessedUtxo.p2wpkhScript,
30250
+ amount: remainingValue
30251
+ });
30252
+ for (let i = 0; i < processedUtxos.length; i++) {
30253
+ const processed = processedUtxos[i];
30254
+ if (!processed) {
30255
+ throw new Error(`Processed UTXO at index ${i} is undefined`);
30256
+ }
30257
+ try {
30258
+ builder.updateInput(i, {
30259
+ witnessScript: processed.p2wpkhScript
30260
+ });
30261
+ builder.signIdx;
30262
+ } catch (error) {
30263
+ throw new Error(`Failed to handle funding UTXO input ${i + 1}: ${error}`);
30264
+ }
30265
+ }
30266
+ let psbtHex;
30267
+ try {
30268
+ psbtHex = (0, import_utils22.bytesToHex)(builder.toPSBT());
30269
+ } catch (error) {
30270
+ throw new Error(`Failed to extract transaction: ${error}`);
30271
+ }
30272
+ return {
30273
+ feeBumpPsbt: psbtHex,
30274
+ usedUtxos: utxos,
30275
+ correctedParentTx: correctedTxHex !== txHex ? correctedTxHex : void 0
30276
+ };
30277
+ }
29257
30278
  // Annotate the CommonJS export names for ESM import in node:
29258
30279
  0 && (module.exports = {
29259
30280
  AuthenticationError,
29260
30281
  ConfigurationError,
30282
+ DEFAULT_FEE_SATS,
29261
30283
  DefaultSparkSigner,
29262
30284
  InternalValidationError,
29263
30285
  LRC_WALLET_NETWORK,
@@ -29281,6 +30303,9 @@ init_buffer();
29281
30303
  collectResponses,
29282
30304
  computeTaprootKeyNoScript,
29283
30305
  computerLagrangeCoefficients,
30306
+ constructFeeBumpTx,
30307
+ constructUnilateralExitFeeBumpPackages,
30308
+ constructUnilateralExitTxs,
29284
30309
  createRefundTx,
29285
30310
  createSigningCommitment,
29286
30311
  createSigningNonce,
@@ -29299,6 +30324,7 @@ init_buffer();
29299
30324
  getLatestDepositTxId,
29300
30325
  getNetwork,
29301
30326
  getNetworkFromAddress,
30327
+ getNetworkFromString,
29302
30328
  getNextTransactionSequence,
29303
30329
  getP2TRAddressFromPkScript,
29304
30330
  getP2TRAddressFromPublicKey,
@@ -29314,7 +30340,10 @@ init_buffer();
29314
30340
  getTxFromRawTxHex,
29315
30341
  getTxId,
29316
30342
  getTxIdNoReverse,
30343
+ isEphemeralAnchorOutput,
30344
+ isTxBroadcast,
29317
30345
  lastKeyWithTarget,
30346
+ maybeApplyFee,
29318
30347
  modInverse,
29319
30348
  proofOfPossessionMessageHashForDepositAddress,
29320
30349
  recoverSecret,