@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
@@ -2,9 +2,7 @@ import {
2
2
  Lrc20ConnectionManager
3
3
  } from "./chunk-FWQPAPXK.js";
4
4
  import {
5
- TokenTransactionService
6
- } from "./chunk-W3EC5XSA.js";
7
- import {
5
+ DEFAULT_FEE_SATS,
8
6
  computeTaprootKeyNoScript,
9
7
  createRefundTx,
10
8
  getEphemeralAnchorOutput,
@@ -19,10 +17,7 @@ import {
19
17
  getTxFromRawTxHex,
20
18
  getTxId,
21
19
  proofOfPossessionMessageHashForDepositAddress
22
- } from "./chunk-E5SL7XTO.js";
23
- import {
24
- calculateAvailableTokenAmount
25
- } from "./chunk-57XLH3ZR.js";
20
+ } from "./chunk-ED3ZAFDI.js";
26
21
  import {
27
22
  mapTransferToWalletTransfer,
28
23
  mapTreeNodeToWalletLeaf
@@ -41,26 +36,29 @@ import {
41
36
  LightningReceiveRequestFromJson,
42
37
  LightningSendRequestFromJson
43
38
  } from "./chunk-S7KD6DDL.js";
39
+ import {
40
+ TokenTransactionService
41
+ } from "./chunk-5MNQB2T4.js";
42
+ import {
43
+ calculateAvailableTokenAmount
44
+ } from "./chunk-57XLH3ZR.js";
44
45
  import {
45
46
  decodeSparkAddress,
46
47
  encodeSparkAddress
47
- } from "./chunk-LIP2K6KR.js";
48
+ } from "./chunk-2CDJZQN4.js";
48
49
  import {
49
50
  WalletConfigService
50
- } from "./chunk-DI7QXUQJ.js";
51
+ } from "./chunk-W2VXS35Y.js";
51
52
  import {
52
53
  LRC_WALLET_NETWORK,
53
54
  LRC_WALLET_NETWORK_TYPE,
54
55
  Network,
55
56
  NetworkToProto,
56
57
  getNetwork
57
- } from "./chunk-VJTDG4BQ.js";
58
+ } from "./chunk-HK6LPV6Z.js";
58
59
  import {
59
60
  ELECTRS_CREDENTIALS
60
61
  } from "./chunk-ZUVYYR5T.js";
61
- import {
62
- BitcoinNetwork_default
63
- } from "./chunk-HMLOC6TE.js";
64
62
  import {
65
63
  addPublicKeys,
66
64
  applyAdaptorToSignature,
@@ -72,12 +70,11 @@ import {
72
70
  getCrypto
73
71
  } from "./chunk-MGPRLH6Q.js";
74
72
  import {
75
- ConnectionManager
76
- } from "./chunk-RGWBSZIO.js";
73
+ BitcoinNetwork_default
74
+ } from "./chunk-HMLOC6TE.js";
77
75
  import {
78
- SendLeafKeyTweaks,
79
- networkToJSON
80
- } from "./chunk-C5LTJBI7.js";
76
+ ConnectionManager
77
+ } from "./chunk-I4JI6TYN.js";
81
78
  import {
82
79
  isReactNative
83
80
  } from "./chunk-HKAKEKCE.js";
@@ -88,6 +85,10 @@ import {
88
85
  RPCError,
89
86
  ValidationError
90
87
  } from "./chunk-TWF35O6M.js";
88
+ import {
89
+ SendLeafKeyTweaks,
90
+ networkToJSON
91
+ } from "./chunk-C5LTJBI7.js";
91
92
  import {
92
93
  Buffer
93
94
  } from "./chunk-MVRQ5US7.js";
@@ -1058,6 +1059,32 @@ var BaseTransferService = class {
1058
1059
  }
1059
1060
  return updatedTransfer;
1060
1061
  }
1062
+ async deliverTransferPackage(transfer, leaves, refundSignatureMap) {
1063
+ const keyTweakInputMap = await this.prepareSendTransferKeyTweaks(
1064
+ transfer.id,
1065
+ transfer.receiverIdentityPublicKey,
1066
+ leaves,
1067
+ refundSignatureMap
1068
+ );
1069
+ const transferPackage = await this.prepareTransferPackage(
1070
+ transfer.id,
1071
+ keyTweakInputMap,
1072
+ leaves,
1073
+ transfer.receiverIdentityPublicKey
1074
+ );
1075
+ const sparkClient = await this.connectionManager.createSparkClient(
1076
+ this.config.getCoordinatorAddress()
1077
+ );
1078
+ const response = await sparkClient.finalize_transfer_with_transfer_package({
1079
+ transferId: transfer.id,
1080
+ ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
1081
+ transferPackage
1082
+ });
1083
+ if (!response.transfer) {
1084
+ throw new ValidationError("No transfer response from operator");
1085
+ }
1086
+ return response.transfer;
1087
+ }
1061
1088
  async sendTransferWithKeyTweaks(leaves, receiverIdentityPubkey) {
1062
1089
  const transferID = uuidv7();
1063
1090
  const keyTweakInputMap = await this.prepareSendTransferKeyTweaks(
@@ -1815,9 +1842,20 @@ var TransferService = class extends BaseTransferService {
1815
1842
  if (!input) {
1816
1843
  throw Error("Could not fetch tx input");
1817
1844
  }
1818
- const newTx = new Transaction({ allowUnknownOutputs: true });
1819
- for (let j = 0; j < nodeTx.outputsLength; j++) {
1820
- newTx.addOutput(nodeTx.getOutput(j));
1845
+ const newTx = new Transaction({ version: 3, allowUnknownOutputs: true });
1846
+ const originalOutput = nodeTx.getOutput(0);
1847
+ if (!originalOutput) {
1848
+ throw Error("Could not get original output");
1849
+ }
1850
+ newTx.addOutput({
1851
+ script: originalOutput.script,
1852
+ amount: originalOutput.amount
1853
+ });
1854
+ for (let j = 1; j < nodeTx.outputsLength; j++) {
1855
+ const additionalOutput = nodeTx.getOutput(j);
1856
+ if (additionalOutput) {
1857
+ newTx.addOutput(additionalOutput);
1858
+ }
1821
1859
  }
1822
1860
  if (i === 0) {
1823
1861
  const currSequence = input.sequence;
@@ -1844,9 +1882,23 @@ var TransferService = class extends BaseTransferService {
1844
1882
  throw Error("leaf does not have refund tx");
1845
1883
  }
1846
1884
  const refundTx = getTxFromRawTxBytes(leaf?.refundTx);
1847
- const newRefundTx = new Transaction({ allowUnknownOutputs: true });
1848
- for (let j = 0; j < refundTx.outputsLength; j++) {
1849
- newRefundTx.addOutput(refundTx.getOutput(j));
1885
+ const newRefundTx = new Transaction({
1886
+ version: 3,
1887
+ allowUnknownOutputs: true
1888
+ });
1889
+ const originalRefundOutput = refundTx.getOutput(0);
1890
+ if (!originalRefundOutput) {
1891
+ throw Error("Could not get original refund output");
1892
+ }
1893
+ newRefundTx.addOutput({
1894
+ script: originalRefundOutput.script,
1895
+ amount: originalRefundOutput.amount
1896
+ });
1897
+ for (let j = 1; j < refundTx.outputsLength; j++) {
1898
+ const additionalOutput = refundTx.getOutput(j);
1899
+ if (additionalOutput) {
1900
+ newRefundTx.addOutput(additionalOutput);
1901
+ }
1850
1902
  }
1851
1903
  const refundTxInput = refundTx.getInput(0);
1852
1904
  if (!refundTxInput) {
@@ -1955,10 +2007,11 @@ var TransferService = class extends BaseTransferService {
1955
2007
  nodeTxSignature: leafSignature,
1956
2008
  refundTxSignature: refundSignature
1957
2009
  });
1958
- return await sparkClient.finalize_node_signatures({
2010
+ const result = await sparkClient.finalize_node_signatures({
1959
2011
  intent: 3 /* REFRESH */,
1960
2012
  nodeSignatures
1961
2013
  });
2014
+ return result;
1962
2015
  }
1963
2016
  async extendTimelock(node, signingPubKey) {
1964
2017
  const nodeTx = getTxFromRawTxBytes(node.nodeTx);
@@ -1969,9 +2022,20 @@ var TransferService = class extends BaseTransferService {
1969
2022
  index: 0
1970
2023
  };
1971
2024
  const { nextSequence: newNodeSequence } = getNextTransactionSequence(refundSequence);
1972
- const newNodeTx = new Transaction({ allowUnknownOutputs: true });
2025
+ const newNodeTx = new Transaction({
2026
+ version: 3,
2027
+ allowUnknownOutputs: true
2028
+ });
1973
2029
  newNodeTx.addInput({ ...newNodeOutPoint, sequence: newNodeSequence });
1974
- newNodeTx.addOutput(nodeTx.getOutput(0));
2030
+ const originalOutput = nodeTx.getOutput(0);
2031
+ if (!originalOutput) {
2032
+ throw Error("Could not get original node output");
2033
+ }
2034
+ newNodeTx.addOutput({
2035
+ script: originalOutput.script,
2036
+ amount: originalOutput.amount
2037
+ // feeReducedAmount,
2038
+ });
1975
2039
  newNodeTx.addOutput(getEphemeralAnchorOutput());
1976
2040
  const newRefundOutPoint = {
1977
2041
  txid: hexToBytes(getTxId(newNodeTx)),
@@ -1985,11 +2049,16 @@ var TransferService = class extends BaseTransferService {
1985
2049
  initialSequence(),
1986
2050
  newRefundOutPoint,
1987
2051
  amountSats,
2052
+ // feeReducedRefundAmount,
1988
2053
  signingPubKey,
1989
2054
  this.config.getNetwork()
1990
2055
  );
1991
2056
  const nodeSighash = getSigHashFromTx(newNodeTx, 0, nodeTx.getOutput(0));
1992
- const refundSighash = getSigHashFromTx(newRefundTx, 0, nodeTx.getOutput(0));
2057
+ const refundSighash = getSigHashFromTx(
2058
+ newRefundTx,
2059
+ 0,
2060
+ newNodeTx.getOutput(0)
2061
+ );
1993
2062
  const newNodeSigningJob = {
1994
2063
  signingPublicKey: signingPubKey,
1995
2064
  rawTx: newNodeTx.toBytes(),
@@ -2063,6 +2132,94 @@ var TransferService = class extends BaseTransferService {
2063
2132
  ]
2064
2133
  });
2065
2134
  }
2135
+ async refreshTimelockRefundTx(node, signingPubKey) {
2136
+ const nodeTx = getTxFromRawTxBytes(node.nodeTx);
2137
+ const refundTx = getTxFromRawTxBytes(node.refundTx);
2138
+ const currSequence = refundTx.getInput(0).sequence || 0;
2139
+ const { nextSequence } = getNextTransactionSequence(currSequence);
2140
+ const newRefundTx = new Transaction({
2141
+ version: 3,
2142
+ allowUnknownOutputs: true
2143
+ });
2144
+ const originalRefundOutput = refundTx.getOutput(0);
2145
+ if (!originalRefundOutput) {
2146
+ throw Error("Could not get original refund output");
2147
+ }
2148
+ newRefundTx.addOutput({
2149
+ script: originalRefundOutput.script,
2150
+ amount: originalRefundOutput.amount
2151
+ });
2152
+ for (let j = 1; j < refundTx.outputsLength; j++) {
2153
+ const additionalOutput = refundTx.getOutput(j);
2154
+ if (additionalOutput) {
2155
+ newRefundTx.addOutput(additionalOutput);
2156
+ }
2157
+ }
2158
+ const refundTxInput = refundTx.getInput(0);
2159
+ if (!refundTxInput) {
2160
+ throw Error("refund tx doesn't have input");
2161
+ }
2162
+ newRefundTx.addInput({
2163
+ ...refundTxInput,
2164
+ sequence: nextSequence
2165
+ });
2166
+ const refundSigningJob = {
2167
+ signingPublicKey: signingPubKey,
2168
+ rawTx: newRefundTx.toBytes(),
2169
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
2170
+ };
2171
+ const sparkClient = await this.connectionManager.createSparkClient(
2172
+ this.config.getCoordinatorAddress()
2173
+ );
2174
+ const response = await sparkClient.refresh_timelock({
2175
+ leafId: node.id,
2176
+ ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
2177
+ signingJobs: [refundSigningJob]
2178
+ });
2179
+ if (response.signingResults.length !== 1) {
2180
+ throw Error(
2181
+ `Expected 1 signing result, got ${response.signingResults.length}`
2182
+ );
2183
+ }
2184
+ const signingResult = response.signingResults[0];
2185
+ if (!signingResult || !refundSigningJob.signingNonceCommitment) {
2186
+ throw Error("Signing result or nonce commitment does not exist");
2187
+ }
2188
+ const rawTx = getTxFromRawTxBytes(refundSigningJob.rawTx);
2189
+ const txOut = nodeTx.getOutput(0);
2190
+ const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
2191
+ const userSignature = await this.config.signer.signFrost({
2192
+ message: rawTxSighash,
2193
+ privateAsPubKey: signingPubKey,
2194
+ publicKey: signingPubKey,
2195
+ verifyingKey: signingResult.verifyingKey,
2196
+ selfCommitment: refundSigningJob.signingNonceCommitment,
2197
+ statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
2198
+ adaptorPubKey: new Uint8Array()
2199
+ });
2200
+ const signature = await this.config.signer.aggregateFrost({
2201
+ message: rawTxSighash,
2202
+ statechainSignatures: signingResult.signingResult?.signatureShares,
2203
+ statechainPublicKeys: signingResult.signingResult?.publicKeys,
2204
+ verifyingKey: signingResult.verifyingKey,
2205
+ statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
2206
+ selfCommitment: refundSigningJob.signingNonceCommitment,
2207
+ publicKey: signingPubKey,
2208
+ selfSignature: userSignature,
2209
+ adaptorPubKey: new Uint8Array()
2210
+ });
2211
+ const result = await sparkClient.finalize_node_signatures({
2212
+ intent: 3 /* REFRESH */,
2213
+ nodeSignatures: [
2214
+ {
2215
+ nodeId: node.id,
2216
+ nodeTxSignature: new Uint8Array(),
2217
+ refundTxSignature: signature
2218
+ }
2219
+ ]
2220
+ });
2221
+ return result;
2222
+ }
2066
2223
  };
2067
2224
 
2068
2225
  // src/services/coop-exit.ts
@@ -2349,7 +2506,7 @@ var DepositService = class {
2349
2506
  depositTx,
2350
2507
  vout
2351
2508
  }) {
2352
- const rootTx = new Transaction3();
2509
+ const rootTx = new Transaction3({ version: 3 });
2353
2510
  const output = depositTx.getOutput(vout);
2354
2511
  if (!output) {
2355
2512
  throw new ValidationError("Invalid deposit transaction output", {
@@ -2367,17 +2524,19 @@ var DepositService = class {
2367
2524
  expected: "Output with script and amount"
2368
2525
  });
2369
2526
  }
2527
+ let outputAmount = amount;
2370
2528
  rootTx.addInput({
2371
2529
  txid: getTxId(depositTx),
2372
2530
  index: vout
2373
2531
  });
2374
2532
  rootTx.addOutput({
2375
2533
  script,
2376
- amount
2534
+ amount: outputAmount
2377
2535
  });
2536
+ rootTx.addOutput(getEphemeralAnchorOutput());
2378
2537
  const rootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
2379
2538
  const rootTxSighash = getSigHashFromTx(rootTx, 0, output);
2380
- const refundTx = new Transaction3();
2539
+ const refundTx = new Transaction3({ version: 3 });
2381
2540
  const sequence = 1 << 30 | INITIAL_TIME_LOCK2;
2382
2541
  refundTx.addInput({
2383
2542
  txid: getTxId(rootTx),
@@ -2392,10 +2551,11 @@ var DepositService = class {
2392
2551
  const refundPkScript = btc.OutScript.encode(refundAddress);
2393
2552
  refundTx.addOutput({
2394
2553
  script: refundPkScript,
2395
- amount
2554
+ amount: outputAmount
2396
2555
  });
2556
+ refundTx.addOutput(getEphemeralAnchorOutput());
2397
2557
  const refundNonceCommitment = await this.config.signer.getRandomSigningCommitment();
2398
- const refundTxSighash = getSigHashFromTx(refundTx, 0, output);
2558
+ const refundTxSighash = getSigHashFromTx(refundTx, 0, rootTx.getOutput(0));
2399
2559
  const sparkClient = await this.connectionManager.createSparkClient(
2400
2560
  this.config.getCoordinatorAddress()
2401
2561
  );
@@ -2968,6 +3128,12 @@ import { hexToBytes as hexToBytes4 } from "@noble/curves/abstract/utils";
2968
3128
  import { sha256 as sha2566 } from "@noble/hashes/sha2";
2969
3129
  import { Address as Address2, OutScript as OutScript2, Transaction as Transaction4 } from "@scure/btc-signer";
2970
3130
  var INITIAL_TIME_LOCK3 = 2e3;
3131
+ function maybeApplyFee2(amount) {
3132
+ if (amount > BigInt(DEFAULT_FEE_SATS)) {
3133
+ return amount - BigInt(DEFAULT_FEE_SATS);
3134
+ }
3135
+ return amount;
3136
+ }
2971
3137
  var TreeCreationService = class {
2972
3138
  config;
2973
3139
  connectionManager;
@@ -3166,7 +3332,7 @@ var TreeCreationService = class {
3166
3332
  refundTxSigningJob: void 0,
3167
3333
  children: []
3168
3334
  };
3169
- const tx = new Transaction4();
3335
+ const tx = new Transaction4({ version: 3 });
3170
3336
  tx.addInput({
3171
3337
  txid: getTxId(parentTx),
3172
3338
  index: vout
@@ -3178,6 +3344,7 @@ var TreeCreationService = class {
3178
3344
  tx.addOutput({
3179
3345
  script: parentTxOut.script,
3180
3346
  amount: parentTxOut.amount
3347
+ // maybeApplyFee(parentTxOut.amount),
3181
3348
  });
3182
3349
  tx.addOutput(getEphemeralAnchorOutput());
3183
3350
  const signingNonceCommitment = await this.config.signer.getRandomSigningCommitment();
@@ -3194,7 +3361,7 @@ var TreeCreationService = class {
3194
3361
  refundTxSigningJob: void 0,
3195
3362
  children: []
3196
3363
  };
3197
- const childTx = new Transaction4();
3364
+ const childTx = new Transaction4({ version: 3 });
3198
3365
  childTx.addInput({
3199
3366
  txid: getTxId(tx),
3200
3367
  index: 0,
@@ -3203,6 +3370,7 @@ var TreeCreationService = class {
3203
3370
  childTx.addOutput({
3204
3371
  script: parentTxOut.script,
3205
3372
  amount: parentTxOut.amount
3373
+ // maybeApplyFee(parentTxOut.amount),
3206
3374
  });
3207
3375
  childTx.addOutput(getEphemeralAnchorOutput());
3208
3376
  const childSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
@@ -3213,7 +3381,7 @@ var TreeCreationService = class {
3213
3381
  };
3214
3382
  childCreationNode.nodeTxSigningCommitment = childSigningNonceCommitment;
3215
3383
  childCreationNode.nodeTxSigningJob = childSigningJob;
3216
- const refundTx = new Transaction4();
3384
+ const refundTx = new Transaction4({ version: 3 });
3217
3385
  refundTx.addInput({
3218
3386
  txid: getTxId(childTx),
3219
3387
  index: 0,
@@ -3229,8 +3397,9 @@ var TreeCreationService = class {
3229
3397
  const refundPkScript = OutScript2.encode(refundAddress);
3230
3398
  refundTx.addOutput({
3231
3399
  script: refundPkScript,
3232
- amount: parentTxOut.amount
3400
+ amount: maybeApplyFee2(parentTxOut.amount)
3233
3401
  });
3402
+ refundTx.addOutput(getEphemeralAnchorOutput());
3234
3403
  const refundSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
3235
3404
  const refundSigningJob = {
3236
3405
  signingPublicKey: node.signingPublicKey,
@@ -3247,7 +3416,7 @@ var TreeCreationService = class {
3247
3416
  if (!parentTxOutput?.script || !parentTxOutput?.amount) {
3248
3417
  throw new Error("parentTxOutput is undefined");
3249
3418
  }
3250
- const rootNodeTx = new Transaction4();
3419
+ const rootNodeTx = new Transaction4({ version: 3 });
3251
3420
  rootNodeTx.addInput({
3252
3421
  txid: getTxId(parentTx),
3253
3422
  index: vout
@@ -3262,10 +3431,10 @@ var TreeCreationService = class {
3262
3431
  rootNodeTx.addOutput({
3263
3432
  script: childPkScript,
3264
3433
  amount: parentTxOutput.amount / 2n
3434
+ // feeAdjustedAmount / 2n,
3265
3435
  });
3266
3436
  }
3267
- const anchor = getEphemeralAnchorOutput();
3268
- rootNodeTx.addOutput(anchor);
3437
+ rootNodeTx.addOutput(getEphemeralAnchorOutput());
3269
3438
  const rootNodeSigningCommitment = await this.config.signer.getRandomSigningCommitment();
3270
3439
  const rootNodeSigningJob = {
3271
3440
  signingPublicKey: root.signingPublicKey,
@@ -4476,7 +4645,7 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
4476
4645
  adaptorPrivateKey
4477
4646
  );
4478
4647
  }
4479
- await this.transferService.sendTransferTweakKey(
4648
+ await this.transferService.deliverTransferPackage(
4480
4649
  transfer,
4481
4650
  leafKeyTweaks,
4482
4651
  signatureMap
@@ -6161,6 +6330,112 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
6161
6330
  }
6162
6331
  return this.config.signer.validateMessageWithIdentityKey(hash, signature);
6163
6332
  }
6333
+ /**
6334
+ * Signs a transaction with wallet keys.
6335
+ *
6336
+ * @param {string} txHex - The transaction hex to sign
6337
+ * @param {string} keyType - The type of key to use for signing ("identity", "deposit", or "auto-detect")
6338
+ * @returns {Promise<string>} The signed transaction hex
6339
+ */
6340
+ async signTransaction(txHex, keyType = "auto-detect") {
6341
+ try {
6342
+ const tx = Transaction6.fromRaw(hexToBytes7(txHex));
6343
+ let publicKey;
6344
+ switch (keyType.toLowerCase()) {
6345
+ case "identity":
6346
+ publicKey = await this.config.signer.getIdentityPublicKey();
6347
+ break;
6348
+ case "deposit":
6349
+ publicKey = await this.config.signer.getDepositSigningKey();
6350
+ break;
6351
+ case "auto-detect":
6352
+ default:
6353
+ const detectedKey = await this.detectKeyForTransaction(tx);
6354
+ if (detectedKey) {
6355
+ publicKey = detectedKey.publicKey;
6356
+ } else {
6357
+ publicKey = await this.config.signer.getIdentityPublicKey();
6358
+ }
6359
+ break;
6360
+ }
6361
+ let inputsSigned = 0;
6362
+ for (let i = 0; i < tx.inputsLength; i++) {
6363
+ const input = tx.getInput(i);
6364
+ if (!input?.witnessUtxo?.script) {
6365
+ continue;
6366
+ }
6367
+ const script = input.witnessUtxo.script;
6368
+ if (script.length === 1 && script[0] === 81) {
6369
+ continue;
6370
+ }
6371
+ const identityScript = getP2TRScriptFromPublicKey(
6372
+ publicKey,
6373
+ this.config.getNetwork()
6374
+ );
6375
+ if (bytesToHex4(script) === bytesToHex4(identityScript)) {
6376
+ try {
6377
+ this.config.signer.signTransactionIndex(tx, i, publicKey);
6378
+ inputsSigned++;
6379
+ } catch (error) {
6380
+ throw new ValidationError(`Failed to sign input ${i}: ${error}`, {
6381
+ field: "input",
6382
+ value: i
6383
+ });
6384
+ }
6385
+ }
6386
+ }
6387
+ if (inputsSigned === 0) {
6388
+ throw new Error(
6389
+ "No inputs were signed. Check that the transaction contains inputs controlled by this wallet."
6390
+ );
6391
+ }
6392
+ tx.finalize();
6393
+ const signedTxHex = tx.hex;
6394
+ return signedTxHex;
6395
+ } catch (error) {
6396
+ console.error("\u274C Error signing transaction:", error);
6397
+ throw error;
6398
+ }
6399
+ }
6400
+ /**
6401
+ * Helper method to auto-detect which key should be used for signing a transaction.
6402
+ */
6403
+ async detectKeyForTransaction(tx) {
6404
+ try {
6405
+ const identityPubKey = await this.config.signer.getIdentityPublicKey();
6406
+ const depositPubKey = await this.config.signer.getDepositSigningKey();
6407
+ for (let i = 0; i < tx.inputsLength; i++) {
6408
+ const input = tx.getInput(i);
6409
+ if (input?.witnessUtxo?.script) {
6410
+ const script = input.witnessUtxo.script;
6411
+ const identityScript = getP2TRScriptFromPublicKey(
6412
+ identityPubKey,
6413
+ this.config.getNetwork()
6414
+ );
6415
+ const depositScript = getP2TRScriptFromPublicKey(
6416
+ depositPubKey,
6417
+ this.config.getNetwork()
6418
+ );
6419
+ if (bytesToHex4(script) === bytesToHex4(identityScript)) {
6420
+ return {
6421
+ publicKey: identityPubKey,
6422
+ keyType: "identity"
6423
+ };
6424
+ }
6425
+ if (bytesToHex4(script) === bytesToHex4(depositScript)) {
6426
+ return {
6427
+ publicKey: depositPubKey,
6428
+ keyType: "deposit"
6429
+ };
6430
+ }
6431
+ }
6432
+ }
6433
+ return null;
6434
+ } catch (error) {
6435
+ console.warn("Error during key auto-detection:", error);
6436
+ return null;
6437
+ }
6438
+ }
6164
6439
  /**
6165
6440
  * Get a Lightning receive request by ID.
6166
6441
  *
@@ -6191,6 +6466,246 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
6191
6466
  const sspClient = this.getSspClient();
6192
6467
  return await sspClient.getCoopExitRequest(id);
6193
6468
  }
6469
+ /**
6470
+ * Check the remaining timelock on a given node.
6471
+ *
6472
+ * @param {string} nodeId - The ID of the node to check
6473
+ * @returns {Promise<{nodeTimelock: number, refundTimelock: number}>} The remaining timelocks in blocks for both node and refund transactions
6474
+ */
6475
+ async checkTimelock(nodeId) {
6476
+ const sparkClient = await this.connectionManager.createSparkClient(
6477
+ this.config.getCoordinatorAddress()
6478
+ );
6479
+ try {
6480
+ const response = await sparkClient.query_nodes({
6481
+ source: {
6482
+ $case: "nodeIds",
6483
+ nodeIds: {
6484
+ nodeIds: [nodeId]
6485
+ }
6486
+ },
6487
+ includeParents: false,
6488
+ network: NetworkToProto[this.config.getNetwork()]
6489
+ });
6490
+ const node = response.nodes[nodeId];
6491
+ if (!node) {
6492
+ throw new ValidationError("Node not found", {
6493
+ field: "nodeId",
6494
+ value: nodeId
6495
+ });
6496
+ }
6497
+ const isRootNode = !node.parentNodeId;
6498
+ if (!node.nodeTx || node.nodeTx.length === 0) {
6499
+ throw new ValidationError(
6500
+ `Node transaction data is missing or empty for ${isRootNode ? "root" : "non-root"} node`,
6501
+ {
6502
+ field: "nodeTx",
6503
+ value: node.nodeTx?.length || 0
6504
+ }
6505
+ );
6506
+ }
6507
+ if (!node.refundTx || node.refundTx.length === 0) {
6508
+ throw new ValidationError(
6509
+ `Refund transaction data is missing or empty for ${isRootNode ? "root" : "non-root"} node`,
6510
+ {
6511
+ field: "refundTx",
6512
+ value: node.refundTx?.length || 0
6513
+ }
6514
+ );
6515
+ }
6516
+ let nodeTx, refundTx;
6517
+ try {
6518
+ nodeTx = getTxFromRawTxBytes(node.nodeTx);
6519
+ } catch (error) {
6520
+ throw new ValidationError(
6521
+ `Failed to parse node transaction for ${isRootNode ? "root" : "non-root"} node: ${error instanceof Error ? error.message : String(error)}`,
6522
+ {
6523
+ field: "nodeTx",
6524
+ value: node.nodeTx.length
6525
+ }
6526
+ );
6527
+ }
6528
+ try {
6529
+ refundTx = getTxFromRawTxBytes(node.refundTx);
6530
+ } catch (error) {
6531
+ throw new ValidationError(
6532
+ `Failed to parse refund transaction for ${isRootNode ? "root" : "non-root"} node: ${error instanceof Error ? error.message : String(error)}`,
6533
+ {
6534
+ field: "refundTx",
6535
+ value: node.refundTx.length
6536
+ }
6537
+ );
6538
+ }
6539
+ const nodeInput = nodeTx.getInput(0);
6540
+ if (!nodeInput) {
6541
+ throw new ValidationError(
6542
+ `Node transaction has no inputs for ${isRootNode ? "root" : "non-root"} node`,
6543
+ {
6544
+ field: "nodeInput",
6545
+ value: nodeTx.inputsLength
6546
+ }
6547
+ );
6548
+ }
6549
+ if (!nodeInput.sequence) {
6550
+ throw new ValidationError(
6551
+ `Node transaction has no sequence for ${isRootNode ? "root" : "non-root"} node`,
6552
+ {
6553
+ field: "sequence",
6554
+ value: nodeInput.sequence
6555
+ }
6556
+ );
6557
+ }
6558
+ const refundInput = refundTx.getInput(0);
6559
+ if (!refundInput) {
6560
+ throw new ValidationError(
6561
+ `Refund transaction has no inputs for ${isRootNode ? "root" : "non-root"} node`,
6562
+ {
6563
+ field: "refundInput",
6564
+ value: refundTx.inputsLength
6565
+ }
6566
+ );
6567
+ }
6568
+ if (!refundInput.sequence) {
6569
+ throw new ValidationError(
6570
+ `Refund transaction has no sequence for ${isRootNode ? "root" : "non-root"} node`,
6571
+ {
6572
+ field: "sequence",
6573
+ value: refundInput.sequence
6574
+ }
6575
+ );
6576
+ }
6577
+ const nodeTimelock = nodeInput.sequence & 65535;
6578
+ const refundTimelock = refundInput.sequence & 65535;
6579
+ return {
6580
+ nodeTimelock,
6581
+ refundTimelock
6582
+ };
6583
+ } catch (error) {
6584
+ throw new NetworkError(
6585
+ `Failed to check timelock for node ${nodeId}`,
6586
+ {
6587
+ method: "query_nodes"
6588
+ },
6589
+ error
6590
+ );
6591
+ }
6592
+ }
6593
+ /**
6594
+ * Refresh the timelock of a specific node.
6595
+ *
6596
+ * @param {string} nodeId - The ID of the node to refresh
6597
+ * @returns {Promise<void>} Promise that resolves when the timelock is refreshed
6598
+ */
6599
+ async testOnly_expireTimelock(nodeId) {
6600
+ const sparkClient = await this.connectionManager.createSparkClient(
6601
+ this.config.getCoordinatorAddress()
6602
+ );
6603
+ try {
6604
+ const response = await sparkClient.query_nodes({
6605
+ source: {
6606
+ $case: "nodeIds",
6607
+ nodeIds: {
6608
+ nodeIds: [nodeId]
6609
+ }
6610
+ },
6611
+ includeParents: true
6612
+ });
6613
+ const node = response.nodes[nodeId];
6614
+ if (!node) {
6615
+ throw new ValidationError("Node not found", {
6616
+ field: "nodeId",
6617
+ value: nodeId
6618
+ });
6619
+ }
6620
+ if (!node.parentNodeId) {
6621
+ throw new ValidationError("Node has no parent", {
6622
+ field: "parentNodeId",
6623
+ value: node.parentNodeId
6624
+ });
6625
+ }
6626
+ const parentNode = response.nodes[node.parentNodeId];
6627
+ if (!parentNode) {
6628
+ throw new ValidationError("Parent node not found", {
6629
+ field: "parentNodeId",
6630
+ value: node.parentNodeId
6631
+ });
6632
+ }
6633
+ const signingPubKey = await this.config.signer.generatePublicKey(
6634
+ sha2567(node.id)
6635
+ );
6636
+ const result = await this.transferService.refreshTimelockNodes(
6637
+ [node],
6638
+ parentNode,
6639
+ signingPubKey
6640
+ );
6641
+ const leafIndex = this.leaves.findIndex((leaf) => leaf.id === node.id);
6642
+ if (leafIndex !== -1 && result.nodes.length > 0) {
6643
+ const newNode = result.nodes[0];
6644
+ if (newNode) {
6645
+ this.leaves[leafIndex] = newNode;
6646
+ }
6647
+ }
6648
+ } catch (error) {
6649
+ throw new NetworkError(
6650
+ "Failed to refresh timelock",
6651
+ {
6652
+ method: "refresh_timelock"
6653
+ },
6654
+ error
6655
+ );
6656
+ }
6657
+ }
6658
+ /**
6659
+ * Refresh the timelock of a specific node's refund transaction only.
6660
+ *
6661
+ * @param {string} nodeId - The ID of the node whose refund transaction to refresh
6662
+ * @returns {Promise<void>} Promise that resolves when the refund timelock is refreshed
6663
+ */
6664
+ async testOnly_expireTimelockRefundTx(nodeId) {
6665
+ const sparkClient = await this.connectionManager.createSparkClient(
6666
+ this.config.getCoordinatorAddress()
6667
+ );
6668
+ try {
6669
+ const response = await sparkClient.query_nodes({
6670
+ source: {
6671
+ $case: "nodeIds",
6672
+ nodeIds: {
6673
+ nodeIds: [nodeId]
6674
+ }
6675
+ },
6676
+ includeParents: false
6677
+ });
6678
+ const node = response.nodes[nodeId];
6679
+ if (!node) {
6680
+ throw new ValidationError("Node not found", {
6681
+ field: "nodeId",
6682
+ value: nodeId
6683
+ });
6684
+ }
6685
+ const signingPubKey = await this.config.signer.generatePublicKey(
6686
+ sha2567(node.id)
6687
+ );
6688
+ const result = await this.transferService.refreshTimelockRefundTx(
6689
+ node,
6690
+ signingPubKey
6691
+ );
6692
+ const leafIndex = this.leaves.findIndex((leaf) => leaf.id === node.id);
6693
+ if (leafIndex !== -1 && result.nodes.length > 0) {
6694
+ const newNode = result.nodes[0];
6695
+ if (newNode) {
6696
+ this.leaves[leafIndex] = newNode;
6697
+ }
6698
+ }
6699
+ } catch (error) {
6700
+ throw new NetworkError(
6701
+ "Failed to refresh refund timelock",
6702
+ {
6703
+ method: "refresh_timelock_refund_tx"
6704
+ },
6705
+ error
6706
+ );
6707
+ }
6708
+ }
6194
6709
  cleanup() {
6195
6710
  if (this.claimTransfersInterval) {
6196
6711
  clearInterval(this.claimTransfersInterval);