@buildonspark/spark-sdk 0.2.3 → 0.2.5

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 (91) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/{chunk-3SEOTO43.js → chunk-3SPMJMUX.js} +3 -2
  3. package/dist/{chunk-CDLETEDT.js → chunk-CQY5ML2A.js} +16 -3
  4. package/dist/{chunk-PTRXJS7Q.js → chunk-LQZL2D3Y.js} +1 -1
  5. package/dist/{chunk-PLLJIZC3.js → chunk-U7LRIWTF.js} +2471 -822
  6. package/dist/{client-CcYzmpmj.d.cts → client-C88GCTPB.d.cts} +211 -104
  7. package/dist/{client-CGTRS23n.d.ts → client-Dg6vS_2I.d.ts} +211 -104
  8. package/dist/debug.cjs +2511 -831
  9. package/dist/debug.d.cts +19 -6
  10. package/dist/debug.d.ts +19 -6
  11. package/dist/debug.js +3 -3
  12. package/dist/graphql/objects/index.cjs +13 -1
  13. package/dist/graphql/objects/index.d.cts +6 -51
  14. package/dist/graphql/objects/index.d.ts +6 -51
  15. package/dist/graphql/objects/index.js +1 -1
  16. package/dist/index.cjs +2491 -797
  17. package/dist/index.d.cts +189 -9
  18. package/dist/index.d.ts +189 -9
  19. package/dist/index.js +32 -4
  20. package/dist/index.node.cjs +2596 -799
  21. package/dist/index.node.d.cts +9 -190
  22. package/dist/index.node.d.ts +9 -190
  23. package/dist/index.node.js +134 -4
  24. package/dist/native/index.cjs +2491 -797
  25. package/dist/native/index.d.cts +309 -174
  26. package/dist/native/index.d.ts +309 -174
  27. package/dist/native/index.js +2495 -814
  28. package/dist/proto/lrc20.d.cts +1 -1
  29. package/dist/proto/lrc20.d.ts +1 -1
  30. package/dist/proto/spark.d.cts +1 -1
  31. package/dist/proto/spark.d.ts +1 -1
  32. package/dist/proto/spark_token.d.cts +1 -1
  33. package/dist/proto/spark_token.d.ts +1 -1
  34. package/dist/{spark-B_7nZx6T.d.cts → spark-ESAfZARg.d.cts} +1 -1
  35. package/dist/{spark-B_7nZx6T.d.ts → spark-ESAfZARg.d.ts} +1 -1
  36. package/dist/{spark-wallet-CxcGPXRB.d.ts → spark-wallet-B2WwKN8W.d.ts} +57 -35
  37. package/dist/{spark-wallet-DJJm19BP.d.cts → spark-wallet-Di65w0Us.d.cts} +57 -35
  38. package/dist/spark-wallet.node-7R0Rxyj9.d.cts +13 -0
  39. package/dist/spark-wallet.node-CSPWOWRu.d.ts +13 -0
  40. package/dist/tests/test-utils.cjs +578 -77
  41. package/dist/tests/test-utils.d.cts +12 -13
  42. package/dist/tests/test-utils.d.ts +12 -13
  43. package/dist/tests/test-utils.js +54 -17
  44. package/dist/types/index.cjs +16 -3
  45. package/dist/types/index.d.cts +3 -4
  46. package/dist/types/index.d.ts +3 -4
  47. package/dist/types/index.js +2 -2
  48. package/dist/{xchain-address-Bh9w1SeC.d.ts → xchain-address-BsveIy5l.d.ts} +56 -8
  49. package/dist/{xchain-address-SZ7dkVUE.d.cts → xchain-address-CqRu3F21.d.cts} +56 -8
  50. package/package.json +1 -1
  51. package/src/graphql/client.ts +57 -8
  52. package/src/graphql/mutations/CompleteLeavesSwap.ts +9 -1
  53. package/src/graphql/mutations/RequestSwapLeaves.ts +4 -0
  54. package/src/graphql/objects/CompleteLeavesSwapInput.ts +34 -34
  55. package/src/graphql/objects/LeavesSwapRequest.ts +4 -0
  56. package/src/graphql/objects/RequestLeavesSwapInput.ts +48 -47
  57. package/src/graphql/objects/SparkWalletUser.ts +1 -1
  58. package/src/graphql/objects/SwapLeaf.ts +40 -32
  59. package/src/graphql/objects/UserLeafInput.ts +24 -0
  60. package/src/graphql/objects/UserRequest.ts +4 -0
  61. package/src/graphql/queries/Transfers.ts +15 -0
  62. package/src/index.node.ts +1 -1
  63. package/src/native/index.ts +4 -5
  64. package/src/services/coop-exit.ts +171 -36
  65. package/src/services/deposit.ts +471 -74
  66. package/src/services/lightning.ts +18 -5
  67. package/src/services/signing.ts +162 -50
  68. package/src/services/transfer.ts +950 -384
  69. package/src/services/tree-creation.ts +342 -121
  70. package/src/spark-wallet/spark-wallet.node.ts +71 -66
  71. package/src/spark-wallet/spark-wallet.ts +561 -192
  72. package/src/tests/integration/coop-exit.test.ts +3 -8
  73. package/src/tests/integration/deposit.test.ts +3 -3
  74. package/src/tests/integration/lightning.test.ts +521 -466
  75. package/src/tests/integration/ssp/static_deposit.test.ts +83 -1
  76. package/src/tests/integration/ssp/transfers.test.ts +97 -0
  77. package/src/tests/integration/swap.test.ts +559 -307
  78. package/src/tests/integration/transfer.test.ts +625 -623
  79. package/src/tests/integration/wallet.test.ts +2 -2
  80. package/src/tests/integration/watchtower.test.ts +211 -0
  81. package/src/tests/test-utils.ts +63 -14
  82. package/src/tests/utils/test-faucet.ts +4 -2
  83. package/src/types/sdk-types.ts +15 -0
  84. package/src/utils/adaptor-signature.ts +15 -5
  85. package/src/utils/bitcoin.ts +13 -0
  86. package/src/utils/fetch.ts +75 -0
  87. package/src/utils/mempool.ts +9 -4
  88. package/src/utils/transaction.ts +388 -26
  89. package/dist/sdk-types-CB9HrW5O.d.cts +0 -44
  90. package/dist/sdk-types-CkRNraXT.d.ts +0 -44
  91. package/src/graphql/queries/Transfer.ts +0 -10
@@ -4,9 +4,10 @@ import {
4
4
  import {
5
5
  mapTransferToWalletTransfer,
6
6
  mapTreeNodeToWalletLeaf
7
- } from "./chunk-3SEOTO43.js";
7
+ } from "./chunk-3SPMJMUX.js";
8
8
  import {
9
9
  BitcoinNetwork_default,
10
+ ClaimStaticDepositFromJson,
10
11
  ClaimStaticDepositRequestType_default,
11
12
  CoopExitFeeQuoteFromJson,
12
13
  CurrencyAmountFromJson,
@@ -22,7 +23,7 @@ import {
22
23
  LightningSendRequestFromJson,
23
24
  SparkCoopExitRequestStatus_default,
24
25
  TransferFromJson
25
- } from "./chunk-CDLETEDT.js";
26
+ } from "./chunk-CQY5ML2A.js";
26
27
  import {
27
28
  Empty,
28
29
  SendLeafKeyTweaks,
@@ -577,7 +578,7 @@ import * as ecies from "eciesjs";
577
578
  import { isNode } from "@lightsparkdev/core";
578
579
  var isReactNative = typeof navigator !== "undefined" && navigator.product === "ReactNative";
579
580
  var isBun = globalThis.Bun !== void 0;
580
- var packageVersion = true ? "0.2.3" : "unknown";
581
+ var packageVersion = true ? "0.2.5" : "unknown";
581
582
  var baseEnvStr = "unknown";
582
583
  if (isBun) {
583
584
  const bunVersion = "version" in globalThis.Bun ? globalThis.Bun.version : "unknown-version";
@@ -2911,10 +2912,23 @@ function getTxId(tx) {
2911
2912
  function getTxIdNoReverse(tx) {
2912
2913
  return bytesToHex4(sha2563(sha2563(tx.toBytes(true))));
2913
2914
  }
2915
+ function getTxEstimatedVbytesSizeByNumberOfInputsOutputs(numInputs, numOutputs) {
2916
+ const TX_OVERHEAD = 10;
2917
+ const IN_VBYTES = 150;
2918
+ const OUT_VBYTES = 34;
2919
+ return TX_OVERHEAD + numInputs * IN_VBYTES + numOutputs * OUT_VBYTES;
2920
+ }
2914
2921
 
2915
2922
  // src/utils/transaction.ts
2916
2923
  import { Transaction as Transaction2 } from "@scure/btc-signer";
2924
+ var INITIAL_TIMELOCK = 2e3;
2925
+ var TEST_UNILATERAL_TIMELOCK = 100;
2917
2926
  var TIME_LOCK_INTERVAL = 100;
2927
+ var DIRECT_TIMELOCK_OFFSET = 50;
2928
+ var INITIAL_SEQUENCE = 1 << 30 | INITIAL_TIMELOCK;
2929
+ var INITIAL_DIRECT_SEQUENCE = 1 << 30 | INITIAL_TIMELOCK + DIRECT_TIMELOCK_OFFSET;
2930
+ var TEST_UNILATERAL_SEQUENCE = 1 << 30 | TEST_UNILATERAL_TIMELOCK;
2931
+ var TEST_UNILATERAL_DIRECT_SEQUENCE = 1 << 30 | TEST_UNILATERAL_TIMELOCK + DIRECT_TIMELOCK_OFFSET;
2918
2932
  var ESTIMATED_TX_SIZE = 191;
2919
2933
  var DEFAULT_SATS_PER_VBYTE = 5;
2920
2934
  var DEFAULT_FEE_SATS = ESTIMATED_TX_SIZE * DEFAULT_SATS_PER_VBYTE;
@@ -2924,29 +2938,270 @@ function maybeApplyFee(amount) {
2924
2938
  }
2925
2939
  return amount;
2926
2940
  }
2927
- function createRefundTx(sequence, nodeOutPoint, amountSats, receivingPubkey, network) {
2928
- const newRefundTx = new Transaction2({
2941
+ function createRootTx(depositOutPoint, depositTxOut) {
2942
+ const cpfpRootTx = new Transaction2({
2943
+ version: 3,
2944
+ allowUnknownOutputs: true
2945
+ });
2946
+ cpfpRootTx.addInput(depositOutPoint);
2947
+ cpfpRootTx.addOutput(depositTxOut);
2948
+ cpfpRootTx.addOutput(getEphemeralAnchorOutput());
2949
+ const directRootTx = new Transaction2({
2950
+ version: 3,
2951
+ allowUnknownOutputs: true
2952
+ });
2953
+ directRootTx.addInput(depositOutPoint);
2954
+ directRootTx.addOutput({
2955
+ script: depositTxOut.script,
2956
+ amount: maybeApplyFee(depositTxOut.amount ?? 0n)
2957
+ });
2958
+ return [cpfpRootTx, directRootTx];
2959
+ }
2960
+ function createSplitTx(parentOutPoint, childTxOuts) {
2961
+ const cpfpSplitTx = new Transaction2({
2962
+ version: 3,
2963
+ allowUnknownOutputs: true
2964
+ });
2965
+ cpfpSplitTx.addInput(parentOutPoint);
2966
+ for (const txOut of childTxOuts) {
2967
+ cpfpSplitTx.addOutput(txOut);
2968
+ }
2969
+ cpfpSplitTx.addOutput(getEphemeralAnchorOutput());
2970
+ const directSplitTx = new Transaction2({
2971
+ version: 3,
2972
+ allowUnknownOutputs: true
2973
+ });
2974
+ directSplitTx.addInput(parentOutPoint);
2975
+ let totalOutputAmount = 0n;
2976
+ for (const txOut of childTxOuts) {
2977
+ totalOutputAmount += txOut.amount ?? 0n;
2978
+ }
2979
+ if (totalOutputAmount > BigInt(DEFAULT_FEE_SATS)) {
2980
+ const feeRatio = Number(DEFAULT_FEE_SATS) / Number(totalOutputAmount);
2981
+ for (const txOut of childTxOuts) {
2982
+ const adjustedAmount = BigInt(
2983
+ Math.floor(Number(txOut.amount ?? 0n) * (1 - feeRatio))
2984
+ );
2985
+ directSplitTx.addOutput({
2986
+ script: txOut.script,
2987
+ amount: adjustedAmount
2988
+ });
2989
+ }
2990
+ } else {
2991
+ for (const txOut of childTxOuts) {
2992
+ directSplitTx.addOutput(txOut);
2993
+ }
2994
+ }
2995
+ return [cpfpSplitTx, directSplitTx];
2996
+ }
2997
+ function createNodeTx({
2998
+ txOut,
2999
+ parentOutPoint,
3000
+ applyFee,
3001
+ includeAnchor
3002
+ }) {
3003
+ const nodeTx = new Transaction2({
3004
+ version: 3,
3005
+ allowUnknownOutputs: true
3006
+ });
3007
+ nodeTx.addInput(parentOutPoint);
3008
+ if (applyFee) {
3009
+ nodeTx.addOutput({
3010
+ script: txOut.script,
3011
+ amount: maybeApplyFee(txOut.amount ?? 0n)
3012
+ });
3013
+ } else {
3014
+ nodeTx.addOutput(txOut);
3015
+ }
3016
+ if (includeAnchor) {
3017
+ nodeTx.addOutput(getEphemeralAnchorOutput());
3018
+ }
3019
+ return nodeTx;
3020
+ }
3021
+ function createNodeTxs(txOut, txIn, directTxIn) {
3022
+ const cpfpNodeTx = createNodeTx({
3023
+ txOut,
3024
+ parentOutPoint: txIn,
3025
+ includeAnchor: true
3026
+ });
3027
+ let directNodeTx;
3028
+ if (directTxIn) {
3029
+ directNodeTx = createNodeTx({
3030
+ txOut,
3031
+ parentOutPoint: directTxIn,
3032
+ includeAnchor: false,
3033
+ applyFee: true
3034
+ });
3035
+ }
3036
+ return { cpfpNodeTx, directNodeTx };
3037
+ }
3038
+ function createLeafNodeTx(sequence, directSequence, parentOutPoint, txOut, shouldCalculateFee) {
3039
+ const cpfpLeafTx = new Transaction2({
3040
+ version: 3,
3041
+ allowUnknownOutputs: true
3042
+ });
3043
+ cpfpLeafTx.addInput({
3044
+ ...parentOutPoint,
3045
+ sequence
3046
+ });
3047
+ cpfpLeafTx.addOutput(txOut);
3048
+ cpfpLeafTx.addOutput(getEphemeralAnchorOutput());
3049
+ const directLeafTx = new Transaction2({
3050
+ version: 3,
3051
+ allowUnknownOutputs: true
3052
+ });
3053
+ directLeafTx.addInput({
3054
+ ...parentOutPoint,
3055
+ sequence: directSequence
3056
+ });
3057
+ const amountSats = txOut.amount ?? 0n;
3058
+ let outputAmount = amountSats;
3059
+ if (shouldCalculateFee) {
3060
+ outputAmount = maybeApplyFee(amountSats);
3061
+ }
3062
+ directLeafTx.addOutput({
3063
+ script: txOut.script,
3064
+ amount: outputAmount
3065
+ });
3066
+ return [cpfpLeafTx, directLeafTx];
3067
+ }
3068
+ function createRefundTx({
3069
+ sequence,
3070
+ input,
3071
+ amountSats,
3072
+ receivingPubkey,
3073
+ network,
3074
+ shouldCalculateFee,
3075
+ includeAnchor
3076
+ }) {
3077
+ const refundTx = new Transaction2({
2929
3078
  version: 3,
2930
3079
  allowUnknownOutputs: true
2931
3080
  });
2932
- newRefundTx.addInput({
2933
- ...nodeOutPoint,
3081
+ refundTx.addInput({
3082
+ ...input,
2934
3083
  sequence
2935
3084
  });
2936
3085
  const refundPkScript = getP2TRScriptFromPublicKey(receivingPubkey, network);
2937
- newRefundTx.addOutput({
3086
+ let outputAmount = amountSats;
3087
+ if (shouldCalculateFee) {
3088
+ outputAmount = maybeApplyFee(amountSats);
3089
+ }
3090
+ refundTx.addOutput({
2938
3091
  script: refundPkScript,
3092
+ amount: outputAmount
3093
+ });
3094
+ if (includeAnchor) {
3095
+ refundTx.addOutput(getEphemeralAnchorOutput());
3096
+ }
3097
+ return refundTx;
3098
+ }
3099
+ function createRefundTxs({
3100
+ sequence,
3101
+ directSequence,
3102
+ input,
3103
+ directInput,
3104
+ amountSats,
3105
+ receivingPubkey,
3106
+ network
3107
+ }) {
3108
+ const cpfpRefundTx = createRefundTx({
3109
+ sequence,
3110
+ input,
3111
+ amountSats,
3112
+ receivingPubkey,
3113
+ network,
3114
+ shouldCalculateFee: false,
3115
+ includeAnchor: true
3116
+ });
3117
+ let directRefundTx;
3118
+ let directFromCpfpRefundTx;
3119
+ if (directSequence && directInput) {
3120
+ directRefundTx = createRefundTx({
3121
+ sequence: directSequence,
3122
+ input: directInput,
3123
+ amountSats,
3124
+ receivingPubkey,
3125
+ network,
3126
+ shouldCalculateFee: true,
3127
+ includeAnchor: false
3128
+ });
3129
+ directFromCpfpRefundTx = createRefundTx({
3130
+ sequence: directSequence,
3131
+ input,
3132
+ amountSats,
3133
+ receivingPubkey,
3134
+ network,
3135
+ shouldCalculateFee: true,
3136
+ includeAnchor: false
3137
+ });
3138
+ } else if (directInput && !directSequence) {
3139
+ throw new ValidationError(
3140
+ "directSequence must be provided if directInput is",
3141
+ {
3142
+ field: "directSequence",
3143
+ value: directSequence
3144
+ }
3145
+ );
3146
+ }
3147
+ return { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx };
3148
+ }
3149
+ function createConnectorRefundTransactions(sequence, cpfpNodeOutPoint, directNodeOutPoint, connectorOutput, amountSats, receiverPubKey, network, shouldCalculateFee) {
3150
+ const cpfpRefundTx = new Transaction2({
3151
+ version: 3,
3152
+ allowUnknownOutputs: true
3153
+ });
3154
+ cpfpRefundTx.addInput({
3155
+ ...cpfpNodeOutPoint,
3156
+ sequence
3157
+ });
3158
+ cpfpRefundTx.addInput(connectorOutput);
3159
+ const receiverScript = getP2TRScriptFromPublicKey(receiverPubKey, network);
3160
+ cpfpRefundTx.addOutput({
3161
+ script: receiverScript,
2939
3162
  amount: amountSats
2940
3163
  });
2941
- newRefundTx.addOutput(getEphemeralAnchorOutput());
2942
- return newRefundTx;
3164
+ const directRefundTx = new Transaction2({
3165
+ version: 3,
3166
+ allowUnknownOutputs: true
3167
+ });
3168
+ directRefundTx.addInput({
3169
+ ...directNodeOutPoint,
3170
+ sequence
3171
+ });
3172
+ directRefundTx.addInput(connectorOutput);
3173
+ let outputAmount = amountSats;
3174
+ if (shouldCalculateFee) {
3175
+ outputAmount = maybeApplyFee(amountSats);
3176
+ }
3177
+ directRefundTx.addOutput({
3178
+ script: receiverScript,
3179
+ amount: outputAmount
3180
+ });
3181
+ const directFromCpfpTx = new Transaction2({
3182
+ version: 3,
3183
+ allowUnknownOutputs: true
3184
+ });
3185
+ directFromCpfpTx.addInput({
3186
+ ...cpfpNodeOutPoint,
3187
+ sequence
3188
+ });
3189
+ directFromCpfpTx.addInput(connectorOutput);
3190
+ directFromCpfpTx.addOutput({
3191
+ script: receiverScript,
3192
+ amount: outputAmount
3193
+ });
3194
+ return [cpfpRefundTx, directRefundTx, directFromCpfpTx];
2943
3195
  }
2944
3196
  function getCurrentTimelock(currSequence) {
2945
3197
  return (currSequence || 0) & 65535;
2946
3198
  }
2947
3199
  function getTransactionSequence(currSequence) {
2948
3200
  const timelock = getCurrentTimelock(currSequence);
2949
- return 1 << 30 | timelock;
3201
+ return {
3202
+ nextSequence: 1 << 30 | timelock,
3203
+ nextDirectSequence: 1 << 30 | timelock + DIRECT_TIMELOCK_OFFSET
3204
+ };
2950
3205
  }
2951
3206
  function checkIfValidSequence(currSequence) {
2952
3207
  const TIME_LOCK_ACTIVE = (currSequence || 0) & 2147483648;
@@ -2964,24 +3219,32 @@ function checkIfValidSequence(currSequence) {
2964
3219
  });
2965
3220
  }
2966
3221
  }
2967
- function getNextTransactionSequence(currSequence, forRefresh) {
3222
+ function doesLeafNeedRefresh(currSequence, isNodeTx) {
2968
3223
  const currentTimelock = getCurrentTimelock(currSequence);
2969
- const nextTimelock = currentTimelock - TIME_LOCK_INTERVAL;
2970
- if (forRefresh && nextTimelock <= 100 && currentTimelock > 0) {
2971
- return {
2972
- nextSequence: 1 << 30 | nextTimelock,
2973
- needRefresh: true
2974
- };
3224
+ if (isNodeTx) {
3225
+ return currentTimelock === 0;
2975
3226
  }
2976
- if (nextTimelock < 0) {
3227
+ return currentTimelock <= 100;
3228
+ }
3229
+ function getNextTransactionSequence(currSequence, isNodeTx) {
3230
+ const currentTimelock = getCurrentTimelock(currSequence);
3231
+ const nextTimelock = currentTimelock - TIME_LOCK_INTERVAL;
3232
+ if (isNodeTx && nextTimelock < 0) {
2977
3233
  throw new ValidationError("timelock interval is less than 0", {
2978
3234
  field: "nextTimelock",
2979
- value: nextTimelock
3235
+ value: nextTimelock,
3236
+ expected: "Non-negative timelock interval"
3237
+ });
3238
+ } else if (!isNodeTx && nextTimelock <= 0) {
3239
+ throw new ValidationError("timelock interval is less than or equal to 0", {
3240
+ field: "nextTimelock",
3241
+ value: nextTimelock,
3242
+ expected: "Timelock greater than 0"
2980
3243
  });
2981
3244
  }
2982
3245
  return {
2983
3246
  nextSequence: 1 << 30 | nextTimelock,
2984
- needRefresh: nextTimelock <= 100
3247
+ nextDirectSequence: 1 << 30 | nextTimelock + DIRECT_TIMELOCK_OFFSET
2985
3248
  };
2986
3249
  }
2987
3250
  function getEphemeralAnchorOutput() {
@@ -3033,10 +3296,8 @@ function proofOfPossessionMessageHashForDepositAddress(userPubkey, operatorPubke
3033
3296
  import { schnorr as schnorr3, secp256k1 as secp256k16 } from "@noble/curves/secp256k1";
3034
3297
  import { sha256 as sha2566 } from "@noble/hashes/sha2";
3035
3298
  import { hexToBytes as hexToBytes4 } from "@noble/hashes/utils";
3036
- import * as btc3 from "@scure/btc-signer";
3037
- import { p2tr as p2tr2, Transaction as Transaction3 } from "@scure/btc-signer";
3299
+ import { p2tr as p2tr2 } from "@scure/btc-signer";
3038
3300
  import { equalBytes as equalBytes3 } from "@scure/btc-signer/utils";
3039
- var INITIAL_TIME_LOCK = 2e3;
3040
3301
  var DepositService = class {
3041
3302
  config;
3042
3303
  connectionManager;
@@ -3154,7 +3415,6 @@ var DepositService = class {
3154
3415
  depositTx,
3155
3416
  vout
3156
3417
  }) {
3157
- const rootTx = new Transaction3({ version: 3 });
3158
3418
  const output = depositTx.getOutput(vout);
3159
3419
  if (!output) {
3160
3420
  throw new ValidationError("Invalid deposit transaction output", {
@@ -3172,39 +3432,59 @@ var DepositService = class {
3172
3432
  expected: "Output with script and amount"
3173
3433
  });
3174
3434
  }
3175
- let outputAmount = amount;
3176
- rootTx.addInput({
3177
- txid: getTxId(depositTx),
3435
+ const depositOutPoint = {
3436
+ txid: hexToBytes4(getTxId(depositTx)),
3178
3437
  index: vout
3179
- });
3180
- rootTx.addOutput({
3438
+ };
3439
+ const depositTxOut = {
3181
3440
  script,
3182
- amount: outputAmount
3183
- });
3184
- rootTx.addOutput(getEphemeralAnchorOutput());
3185
- const rootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
3186
- const rootTxSighash = getSigHashFromTx(rootTx, 0, output);
3187
- const refundTx = new Transaction3({ version: 3 });
3188
- const sequence = 1 << 30 | INITIAL_TIME_LOCK;
3189
- refundTx.addInput({
3190
- txid: getTxId(rootTx),
3191
- index: 0,
3192
- sequence
3193
- });
3441
+ amount
3442
+ };
3443
+ const [cpfpRootTx, directRootTx] = createRootTx(
3444
+ depositOutPoint,
3445
+ depositTxOut
3446
+ );
3447
+ const cpfpRootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
3448
+ const directRootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
3449
+ const cpfpRootTxSighash = getSigHashFromTx(cpfpRootTx, 0, output);
3450
+ const directRootTxSighash = getSigHashFromTx(directRootTx, 0, output);
3194
3451
  const signingPubKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
3195
- const refundP2trAddress = getP2TRAddressFromPublicKey(
3196
- signingPubKey,
3197
- this.config.getNetwork()
3452
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
3453
+ sequence: INITIAL_SEQUENCE,
3454
+ directSequence: INITIAL_DIRECT_SEQUENCE,
3455
+ input: { txid: hexToBytes4(getTxId(cpfpRootTx)), index: 0 },
3456
+ directInput: { txid: hexToBytes4(getTxId(directRootTx)), index: 0 },
3457
+ amountSats: amount,
3458
+ receivingPubkey: signingPubKey,
3459
+ network: this.config.getNetwork()
3460
+ });
3461
+ const cpfpRefundNonceCommitment = await this.config.signer.getRandomSigningCommitment();
3462
+ const directRefundNonceCommitment = await this.config.signer.getRandomSigningCommitment();
3463
+ const directFromCpfpRefundNonceCommitment = await this.config.signer.getRandomSigningCommitment();
3464
+ const cpfpRefundTxSighash = getSigHashFromTx(
3465
+ cpfpRefundTx,
3466
+ 0,
3467
+ cpfpRootTx.getOutput(0)
3468
+ );
3469
+ if (!directRefundTx || !directFromCpfpRefundTx) {
3470
+ throw new ValidationError(
3471
+ "Expected direct refund transactions for tree creation",
3472
+ {
3473
+ field: "directRefundTx",
3474
+ value: directRefundTx
3475
+ }
3476
+ );
3477
+ }
3478
+ const directRefundTxSighash = getSigHashFromTx(
3479
+ directRefundTx,
3480
+ 0,
3481
+ directRootTx.getOutput(0)
3482
+ );
3483
+ const directFromCpfpRefundTxSighash = getSigHashFromTx(
3484
+ directFromCpfpRefundTx,
3485
+ 0,
3486
+ cpfpRootTx.getOutput(0)
3198
3487
  );
3199
- const refundAddress = btc3.Address(getNetwork(this.config.getNetwork())).decode(refundP2trAddress);
3200
- const refundPkScript = btc3.OutScript.encode(refundAddress);
3201
- refundTx.addOutput({
3202
- script: refundPkScript,
3203
- amount: outputAmount
3204
- });
3205
- refundTx.addOutput(getEphemeralAnchorOutput());
3206
- const refundNonceCommitment = await this.config.signer.getRandomSigningCommitment();
3207
- const refundTxSighash = getSigHashFromTx(refundTx, 0, rootTx.getOutput(0));
3208
3488
  const sparkClient = await this.connectionManager.createSparkClient(
3209
3489
  this.config.getCoordinatorAddress()
3210
3490
  );
@@ -3218,14 +3498,29 @@ var DepositService = class {
3218
3498
  network: this.config.getNetworkProto()
3219
3499
  },
3220
3500
  rootTxSigningJob: {
3221
- rawTx: rootTx.toBytes(),
3501
+ rawTx: cpfpRootTx.toBytes(),
3222
3502
  signingPublicKey: signingPubKey,
3223
- signingNonceCommitment: rootNonceCommitment.commitment
3503
+ signingNonceCommitment: cpfpRootNonceCommitment.commitment
3224
3504
  },
3225
3505
  refundTxSigningJob: {
3226
- rawTx: refundTx.toBytes(),
3506
+ rawTx: cpfpRefundTx.toBytes(),
3507
+ signingPublicKey: signingPubKey,
3508
+ signingNonceCommitment: cpfpRefundNonceCommitment.commitment
3509
+ },
3510
+ directRootTxSigningJob: {
3511
+ rawTx: directRootTx.toBytes(),
3512
+ signingPublicKey: signingPubKey,
3513
+ signingNonceCommitment: directRootNonceCommitment.commitment
3514
+ },
3515
+ directRefundTxSigningJob: {
3516
+ rawTx: directRefundTx.toBytes(),
3227
3517
  signingPublicKey: signingPubKey,
3228
- signingNonceCommitment: refundNonceCommitment.commitment
3518
+ signingNonceCommitment: directRefundNonceCommitment.commitment
3519
+ },
3520
+ directFromCpfpRefundTxSigningJob: {
3521
+ rawTx: directFromCpfpRefundTx.toBytes(),
3522
+ signingPublicKey: signingPubKey,
3523
+ signingNonceCommitment: directFromCpfpRefundNonceCommitment.commitment
3229
3524
  }
3230
3525
  });
3231
3526
  } catch (error) {
@@ -3264,6 +3559,30 @@ var DepositService = class {
3264
3559
  }
3265
3560
  );
3266
3561
  }
3562
+ if (!treeResp.rootNodeSignatureShares.directNodeTxSigningResult?.signingNonceCommitments) {
3563
+ throw new ValidationError(
3564
+ "No direct node signing nonce commitments found in tree response",
3565
+ {
3566
+ field: "directNodeTxSigningResult.signingNonceCommitments"
3567
+ }
3568
+ );
3569
+ }
3570
+ if (!treeResp.rootNodeSignatureShares.directRefundTxSigningResult?.signingNonceCommitments) {
3571
+ throw new ValidationError(
3572
+ "No direct refund signing nonce commitments found in tree response",
3573
+ {
3574
+ field: "directRefundTxSigningResult.signingNonceCommitments"
3575
+ }
3576
+ );
3577
+ }
3578
+ if (!treeResp.rootNodeSignatureShares.directFromCpfpRefundTxSigningResult?.signingNonceCommitments) {
3579
+ throw new ValidationError(
3580
+ "No direct from CPFP refund signing nonce commitments found in tree response",
3581
+ {
3582
+ field: "directFromCpfpRefundTxSigningResult.signingNonceCommitments"
3583
+ }
3584
+ );
3585
+ }
3267
3586
  if (!equalBytes3(treeResp.rootNodeSignatureShares.verifyingKey, verifyingKey)) {
3268
3587
  throw new ValidationError("Verifying key mismatch", {
3269
3588
  field: "verifyingKey",
@@ -3271,55 +3590,302 @@ var DepositService = class {
3271
3590
  expected: verifyingKey
3272
3591
  });
3273
3592
  }
3274
- const rootSignature = await this.config.signer.signFrost({
3275
- message: rootTxSighash,
3593
+ const cpfpRootSignature = await this.config.signer.signFrost({
3594
+ message: cpfpRootTxSighash,
3276
3595
  publicKey: signingPubKey,
3277
3596
  keyDerivation,
3278
3597
  verifyingKey,
3279
- selfCommitment: rootNonceCommitment,
3598
+ selfCommitment: cpfpRootNonceCommitment,
3280
3599
  statechainCommitments: treeResp.rootNodeSignatureShares.nodeTxSigningResult.signingNonceCommitments,
3281
3600
  adaptorPubKey: new Uint8Array()
3282
3601
  });
3283
- const refundSignature = await this.config.signer.signFrost({
3284
- message: refundTxSighash,
3602
+ const directRootSignature = await this.config.signer.signFrost({
3603
+ message: directRootTxSighash,
3285
3604
  publicKey: signingPubKey,
3286
3605
  keyDerivation,
3287
3606
  verifyingKey,
3288
- selfCommitment: refundNonceCommitment,
3607
+ selfCommitment: directRootNonceCommitment,
3608
+ statechainCommitments: treeResp.rootNodeSignatureShares.directNodeTxSigningResult.signingNonceCommitments,
3609
+ adaptorPubKey: new Uint8Array()
3610
+ });
3611
+ const cpfpRefundSignature = await this.config.signer.signFrost({
3612
+ message: cpfpRefundTxSighash,
3613
+ publicKey: signingPubKey,
3614
+ keyDerivation,
3615
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
3616
+ selfCommitment: cpfpRefundNonceCommitment,
3289
3617
  statechainCommitments: treeResp.rootNodeSignatureShares.refundTxSigningResult.signingNonceCommitments,
3290
3618
  adaptorPubKey: new Uint8Array()
3291
3619
  });
3292
- const rootAggregate = await this.config.signer.aggregateFrost({
3293
- message: rootTxSighash,
3620
+ const directRefundSignature = await this.config.signer.signFrost({
3621
+ message: directRefundTxSighash,
3622
+ publicKey: signingPubKey,
3623
+ keyDerivation,
3624
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
3625
+ selfCommitment: directRefundNonceCommitment,
3626
+ statechainCommitments: treeResp.rootNodeSignatureShares.directRefundTxSigningResult.signingNonceCommitments,
3627
+ adaptorPubKey: new Uint8Array()
3628
+ });
3629
+ const directFromCpfpRefundSignature = await this.config.signer.signFrost({
3630
+ message: directFromCpfpRefundTxSighash,
3631
+ publicKey: signingPubKey,
3632
+ keyDerivation,
3633
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
3634
+ selfCommitment: directFromCpfpRefundNonceCommitment,
3635
+ statechainCommitments: treeResp.rootNodeSignatureShares.directFromCpfpRefundTxSigningResult.signingNonceCommitments,
3636
+ adaptorPubKey: new Uint8Array()
3637
+ });
3638
+ const cpfpRootAggregate = await this.config.signer.aggregateFrost({
3639
+ message: cpfpRootTxSighash,
3294
3640
  statechainSignatures: treeResp.rootNodeSignatureShares.nodeTxSigningResult.signatureShares,
3295
3641
  statechainPublicKeys: treeResp.rootNodeSignatureShares.nodeTxSigningResult.publicKeys,
3296
3642
  verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
3297
3643
  statechainCommitments: treeResp.rootNodeSignatureShares.nodeTxSigningResult.signingNonceCommitments,
3298
- selfCommitment: rootNonceCommitment,
3644
+ selfCommitment: cpfpRootNonceCommitment,
3299
3645
  publicKey: signingPubKey,
3300
- selfSignature: rootSignature,
3646
+ selfSignature: cpfpRootSignature,
3301
3647
  adaptorPubKey: new Uint8Array()
3302
3648
  });
3303
- const refundAggregate = await this.config.signer.aggregateFrost({
3304
- message: refundTxSighash,
3649
+ const directRootAggregate = await this.config.signer.aggregateFrost({
3650
+ message: directRootTxSighash,
3651
+ statechainSignatures: treeResp.rootNodeSignatureShares.directNodeTxSigningResult.signatureShares,
3652
+ statechainPublicKeys: treeResp.rootNodeSignatureShares.directNodeTxSigningResult.publicKeys,
3653
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
3654
+ statechainCommitments: treeResp.rootNodeSignatureShares.directNodeTxSigningResult.signingNonceCommitments,
3655
+ selfCommitment: directRootNonceCommitment,
3656
+ publicKey: signingPubKey,
3657
+ selfSignature: directRootSignature,
3658
+ adaptorPubKey: new Uint8Array()
3659
+ });
3660
+ const cpfpRefundAggregate = await this.config.signer.aggregateFrost({
3661
+ message: cpfpRefundTxSighash,
3305
3662
  statechainSignatures: treeResp.rootNodeSignatureShares.refundTxSigningResult.signatureShares,
3306
3663
  statechainPublicKeys: treeResp.rootNodeSignatureShares.refundTxSigningResult.publicKeys,
3307
3664
  verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
3308
3665
  statechainCommitments: treeResp.rootNodeSignatureShares.refundTxSigningResult.signingNonceCommitments,
3309
- selfCommitment: refundNonceCommitment,
3666
+ selfCommitment: cpfpRefundNonceCommitment,
3310
3667
  publicKey: signingPubKey,
3311
- selfSignature: refundSignature,
3668
+ selfSignature: cpfpRefundSignature,
3669
+ adaptorPubKey: new Uint8Array()
3670
+ });
3671
+ const directRefundAggregate = await this.config.signer.aggregateFrost({
3672
+ message: directRefundTxSighash,
3673
+ statechainSignatures: treeResp.rootNodeSignatureShares.directRefundTxSigningResult.signatureShares,
3674
+ statechainPublicKeys: treeResp.rootNodeSignatureShares.directRefundTxSigningResult.publicKeys,
3675
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
3676
+ statechainCommitments: treeResp.rootNodeSignatureShares.directRefundTxSigningResult.signingNonceCommitments,
3677
+ selfCommitment: directRefundNonceCommitment,
3678
+ publicKey: signingPubKey,
3679
+ selfSignature: directRefundSignature,
3680
+ adaptorPubKey: new Uint8Array()
3681
+ });
3682
+ const directFromCpfpRefundAggregate = await this.config.signer.aggregateFrost({
3683
+ message: directFromCpfpRefundTxSighash,
3684
+ statechainSignatures: treeResp.rootNodeSignatureShares.directFromCpfpRefundTxSigningResult.signatureShares,
3685
+ statechainPublicKeys: treeResp.rootNodeSignatureShares.directFromCpfpRefundTxSigningResult.publicKeys,
3686
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
3687
+ statechainCommitments: treeResp.rootNodeSignatureShares.directFromCpfpRefundTxSigningResult.signingNonceCommitments,
3688
+ selfCommitment: directFromCpfpRefundNonceCommitment,
3689
+ publicKey: signingPubKey,
3690
+ selfSignature: directFromCpfpRefundSignature,
3312
3691
  adaptorPubKey: new Uint8Array()
3313
3692
  });
3314
3693
  let finalizeResp;
3315
3694
  try {
3316
- finalizeResp = await sparkClient.finalize_node_signatures({
3695
+ finalizeResp = await sparkClient.finalize_node_signatures_v2({
3317
3696
  intent: 0 /* CREATION */,
3318
3697
  nodeSignatures: [
3319
3698
  {
3320
3699
  nodeId: treeResp.rootNodeSignatureShares.nodeId,
3321
- nodeTxSignature: rootAggregate,
3322
- refundTxSignature: refundAggregate
3700
+ nodeTxSignature: cpfpRootAggregate,
3701
+ refundTxSignature: cpfpRefundAggregate,
3702
+ directNodeTxSignature: directRootAggregate,
3703
+ directRefundTxSignature: directRefundAggregate,
3704
+ directFromCpfpRefundTxSignature: directFromCpfpRefundAggregate
3705
+ }
3706
+ ]
3707
+ });
3708
+ } catch (error) {
3709
+ throw new NetworkError(
3710
+ "Failed to finalize node signatures",
3711
+ {
3712
+ operation: "finalize_node_signatures",
3713
+ errorCount: 1,
3714
+ errors: error instanceof Error ? error.message : String(error)
3715
+ },
3716
+ error
3717
+ );
3718
+ }
3719
+ return finalizeResp;
3720
+ }
3721
+ /**
3722
+ * @deprecated
3723
+ * Use createTreeRoot instead.
3724
+ * This is currently only used to test backwards compatibility.
3725
+ */
3726
+ async createTreeWithoutDirectTx({
3727
+ keyDerivation,
3728
+ verifyingKey,
3729
+ depositTx,
3730
+ vout
3731
+ }) {
3732
+ const output = depositTx.getOutput(vout);
3733
+ if (!output) {
3734
+ throw new ValidationError("Invalid deposit transaction output", {
3735
+ field: "vout",
3736
+ value: vout,
3737
+ expected: "Valid output index"
3738
+ });
3739
+ }
3740
+ const script = output.script;
3741
+ const amount = output.amount;
3742
+ if (!script || !amount) {
3743
+ throw new ValidationError("No script or amount found in deposit tx", {
3744
+ field: "output",
3745
+ value: output,
3746
+ expected: "Output with script and amount"
3747
+ });
3748
+ }
3749
+ const depositOutPoint = {
3750
+ txid: hexToBytes4(getTxId(depositTx)),
3751
+ index: vout
3752
+ };
3753
+ const depositTxOut = {
3754
+ script,
3755
+ amount
3756
+ };
3757
+ const [cpfpRootTx, _] = createRootTx(depositOutPoint, depositTxOut);
3758
+ const cpfpRootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
3759
+ const cpfpRootTxSighash = getSigHashFromTx(cpfpRootTx, 0, output);
3760
+ const signingPubKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
3761
+ const { cpfpRefundTx } = createRefundTxs({
3762
+ sequence: INITIAL_SEQUENCE,
3763
+ input: { txid: hexToBytes4(getTxId(cpfpRootTx)), index: 0 },
3764
+ amountSats: amount,
3765
+ receivingPubkey: signingPubKey,
3766
+ network: this.config.getNetwork()
3767
+ });
3768
+ const cpfpRefundNonceCommitment = await this.config.signer.getRandomSigningCommitment();
3769
+ const cpfpRefundTxSighash = getSigHashFromTx(
3770
+ cpfpRefundTx,
3771
+ 0,
3772
+ cpfpRootTx.getOutput(0)
3773
+ );
3774
+ const sparkClient = await this.connectionManager.createSparkClient(
3775
+ this.config.getCoordinatorAddress()
3776
+ );
3777
+ let treeResp;
3778
+ try {
3779
+ treeResp = await sparkClient.start_deposit_tree_creation({
3780
+ identityPublicKey: await this.config.signer.getIdentityPublicKey(),
3781
+ onChainUtxo: {
3782
+ vout,
3783
+ rawTx: depositTx.toBytes(true),
3784
+ network: this.config.getNetworkProto()
3785
+ },
3786
+ rootTxSigningJob: {
3787
+ rawTx: cpfpRootTx.toBytes(),
3788
+ signingPublicKey: signingPubKey,
3789
+ signingNonceCommitment: cpfpRootNonceCommitment.commitment
3790
+ },
3791
+ refundTxSigningJob: {
3792
+ rawTx: cpfpRefundTx.toBytes(),
3793
+ signingPublicKey: signingPubKey,
3794
+ signingNonceCommitment: cpfpRefundNonceCommitment.commitment
3795
+ }
3796
+ });
3797
+ } catch (error) {
3798
+ throw new NetworkError(
3799
+ "Failed to start deposit tree creation",
3800
+ {
3801
+ operation: "start_deposit_tree_creation",
3802
+ errorCount: 1,
3803
+ errors: error instanceof Error ? error.message : String(error)
3804
+ },
3805
+ error
3806
+ );
3807
+ }
3808
+ if (!treeResp.rootNodeSignatureShares?.verifyingKey) {
3809
+ throw new ValidationError("No verifying key found in tree response", {
3810
+ field: "verifyingKey",
3811
+ value: treeResp.rootNodeSignatureShares,
3812
+ expected: "Non-null verifying key"
3813
+ });
3814
+ }
3815
+ if (!treeResp.rootNodeSignatureShares.nodeTxSigningResult?.signingNonceCommitments) {
3816
+ throw new ValidationError(
3817
+ "No signing nonce commitments found in tree response",
3818
+ {
3819
+ field: "nodeTxSigningResult.signingNonceCommitments",
3820
+ value: treeResp.rootNodeSignatureShares.nodeTxSigningResult,
3821
+ expected: "Non-null signing nonce commitments"
3822
+ }
3823
+ );
3824
+ }
3825
+ if (!treeResp.rootNodeSignatureShares.refundTxSigningResult?.signingNonceCommitments) {
3826
+ throw new ValidationError(
3827
+ "No signing nonce commitments found in tree response",
3828
+ {
3829
+ field: "refundTxSigningResult.signingNonceCommitments"
3830
+ }
3831
+ );
3832
+ }
3833
+ if (!equalBytes3(treeResp.rootNodeSignatureShares.verifyingKey, verifyingKey)) {
3834
+ throw new ValidationError("Verifying key mismatch", {
3835
+ field: "verifyingKey",
3836
+ value: treeResp.rootNodeSignatureShares.verifyingKey,
3837
+ expected: verifyingKey
3838
+ });
3839
+ }
3840
+ const cpfpRootSignature = await this.config.signer.signFrost({
3841
+ message: cpfpRootTxSighash,
3842
+ publicKey: signingPubKey,
3843
+ keyDerivation,
3844
+ verifyingKey,
3845
+ selfCommitment: cpfpRootNonceCommitment,
3846
+ statechainCommitments: treeResp.rootNodeSignatureShares.nodeTxSigningResult.signingNonceCommitments,
3847
+ adaptorPubKey: new Uint8Array()
3848
+ });
3849
+ const cpfpRefundSignature = await this.config.signer.signFrost({
3850
+ message: cpfpRefundTxSighash,
3851
+ publicKey: signingPubKey,
3852
+ keyDerivation,
3853
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
3854
+ selfCommitment: cpfpRefundNonceCommitment,
3855
+ statechainCommitments: treeResp.rootNodeSignatureShares.refundTxSigningResult.signingNonceCommitments,
3856
+ adaptorPubKey: new Uint8Array()
3857
+ });
3858
+ const cpfpRootAggregate = await this.config.signer.aggregateFrost({
3859
+ message: cpfpRootTxSighash,
3860
+ statechainSignatures: treeResp.rootNodeSignatureShares.nodeTxSigningResult.signatureShares,
3861
+ statechainPublicKeys: treeResp.rootNodeSignatureShares.nodeTxSigningResult.publicKeys,
3862
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
3863
+ statechainCommitments: treeResp.rootNodeSignatureShares.nodeTxSigningResult.signingNonceCommitments,
3864
+ selfCommitment: cpfpRootNonceCommitment,
3865
+ publicKey: signingPubKey,
3866
+ selfSignature: cpfpRootSignature,
3867
+ adaptorPubKey: new Uint8Array()
3868
+ });
3869
+ const cpfpRefundAggregate = await this.config.signer.aggregateFrost({
3870
+ message: cpfpRefundTxSighash,
3871
+ statechainSignatures: treeResp.rootNodeSignatureShares.refundTxSigningResult.signatureShares,
3872
+ statechainPublicKeys: treeResp.rootNodeSignatureShares.refundTxSigningResult.publicKeys,
3873
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
3874
+ statechainCommitments: treeResp.rootNodeSignatureShares.refundTxSigningResult.signingNonceCommitments,
3875
+ selfCommitment: cpfpRefundNonceCommitment,
3876
+ publicKey: signingPubKey,
3877
+ selfSignature: cpfpRefundSignature,
3878
+ adaptorPubKey: new Uint8Array()
3879
+ });
3880
+ let finalizeResp;
3881
+ try {
3882
+ finalizeResp = await sparkClient.finalize_node_signatures_v2({
3883
+ intent: 0 /* CREATION */,
3884
+ nodeSignatures: [
3885
+ {
3886
+ nodeId: treeResp.rootNodeSignatureShares.nodeId,
3887
+ nodeTxSignature: cpfpRootAggregate,
3888
+ refundTxSignature: cpfpRefundAggregate
3323
3889
  }
3324
3890
  ]
3325
3891
  });
@@ -6021,13 +6587,24 @@ function applyAdaptorToSignature(pubkey, hash, signature, adaptorPrivateKeyBytes
6021
6587
  const adaptorPrivateKey = bytesToNumberBE6(adaptorPrivateKeyBytes);
6022
6588
  const newS = mod(sBigInt + adaptorPrivateKey, secp256k110.CURVE.n);
6023
6589
  const newSig = new Uint8Array([...r, ...numberToBytesBE3(newS, 32)]);
6024
- if (schnorr4.verify(newSig, hash, pubkey)) {
6025
- return newSig;
6590
+ try {
6591
+ if (schnorr4.verify(newSig, hash, pubkey)) {
6592
+ return newSig;
6593
+ }
6594
+ } catch (e) {
6595
+ console.error("[applyAdaptorToSignature] Addition verification failed:", e);
6026
6596
  }
6027
6597
  const altS = mod(sBigInt - adaptorPrivateKey, secp256k110.CURVE.n);
6028
6598
  const altSig = new Uint8Array([...r, ...numberToBytesBE3(altS, 32)]);
6029
- if (schnorr4.verify(altSig, hash, pubkey)) {
6030
- return altSig;
6599
+ try {
6600
+ if (schnorr4.verify(altSig, hash, pubkey)) {
6601
+ return altSig;
6602
+ }
6603
+ } catch (e) {
6604
+ console.error(
6605
+ "[applyAdaptorToSignature] Subtraction verification failed:",
6606
+ e
6607
+ );
6031
6608
  }
6032
6609
  throw new Error("Cannot apply adaptor to signature");
6033
6610
  }
@@ -6106,9 +6683,31 @@ function parseSignature(signature) {
6106
6683
  // src/tests/utils/test-faucet.ts
6107
6684
  import { bytesToHex as bytesToHex8, hexToBytes as hexToBytes7 } from "@noble/curves/abstract/utils";
6108
6685
  import { schnorr as schnorr5, secp256k1 as secp256k111 } from "@noble/curves/secp256k1";
6109
- import * as btc4 from "@scure/btc-signer";
6110
- import { Address as Address3, OutScript as OutScript3, SigHash as SigHash2, Transaction as Transaction4 } from "@scure/btc-signer";
6686
+ import * as btc3 from "@scure/btc-signer";
6687
+ import { Address as Address2, OutScript as OutScript2, SigHash as SigHash2, Transaction as Transaction4 } from "@scure/btc-signer";
6111
6688
  import { taprootTweakPrivKey as taprootTweakPrivKey2 } from "@scure/btc-signer/utils";
6689
+
6690
+ // src/utils/fetch.ts
6691
+ var fetchImpl = typeof window !== "undefined" && window.fetch ? window.fetch.bind(window) : globalThis.fetch ? globalThis.fetch : null;
6692
+ var Headers = globalThis.Headers ?? null;
6693
+ var getFetch = () => {
6694
+ if (!fetchImpl) {
6695
+ throw new Error(
6696
+ "Fetch implementation is not set. Please set it using setFetch()."
6697
+ );
6698
+ }
6699
+ if (!Headers) {
6700
+ throw new Error(
6701
+ "Headers implementation is not set. Please set it using setFetch()."
6702
+ );
6703
+ }
6704
+ return {
6705
+ fetch: fetchImpl,
6706
+ Headers
6707
+ };
6708
+ };
6709
+
6710
+ // src/tests/utils/test-faucet.ts
6112
6711
  var STATIC_FAUCET_KEY = hexToBytes7(
6113
6712
  "deadbeef1337cafe4242424242424242deadbeef1337cafe4242424242424242"
6114
6713
  );
@@ -6252,7 +6851,7 @@ var BitcoinFaucet = class _BitcoinFaucet {
6252
6851
  }
6253
6852
  async sendFaucetCoinToP2WPKHAddress(pubKey) {
6254
6853
  const sendToPubKeyTx = new Transaction4();
6255
- const p2wpkhAddress = btc4.p2wpkh(pubKey, getNetwork(4 /* LOCAL */)).address;
6854
+ const p2wpkhAddress = btc3.p2wpkh(pubKey, getNetwork(4 /* LOCAL */)).address;
6256
6855
  if (!p2wpkhAddress) {
6257
6856
  throw new Error("Invalid P2WPKH address");
6258
6857
  }
@@ -6308,12 +6907,13 @@ var BitcoinFaucet = class _BitcoinFaucet {
6308
6907
  }
6309
6908
  async call(method, params) {
6310
6909
  try {
6311
- const response = await fetch(this.url, {
6910
+ const { fetch: fetch2, Headers: Headers2 } = getFetch();
6911
+ const response = await fetch2(this.url, {
6312
6912
  method: "POST",
6313
- headers: {
6913
+ headers: new Headers2({
6314
6914
  "Content-Type": "application/json",
6315
6915
  Authorization: "Basic " + btoa(`${this.username}:${this.password}`)
6316
- },
6916
+ }),
6317
6917
  body: JSON.stringify({
6318
6918
  jsonrpc: "1.0",
6319
6919
  id: "spark-js",
@@ -6368,10 +6968,10 @@ var BitcoinFaucet = class _BitcoinFaucet {
6368
6968
  const tx = new Transaction4();
6369
6969
  tx.addInput(coin.outpoint);
6370
6970
  const availableAmount = COIN_AMOUNT - FEE_AMOUNT;
6371
- const destinationAddress = Address3(getNetwork(4 /* LOCAL */)).decode(
6971
+ const destinationAddress = Address2(getNetwork(4 /* LOCAL */)).decode(
6372
6972
  address2
6373
6973
  );
6374
- const destinationScript = OutScript3.encode(destinationAddress);
6974
+ const destinationScript = OutScript2.encode(destinationAddress);
6375
6975
  tx.addOutput({
6376
6976
  script: destinationScript,
6377
6977
  amount
@@ -6418,7 +7018,7 @@ import {
6418
7018
  import { secp256k1 as secp256k114 } from "@noble/curves/secp256k1";
6419
7019
  import { validateMnemonic } from "@scure/bip39";
6420
7020
  import { wordlist as wordlist2 } from "@scure/bip39/wordlists/english";
6421
- import { Address as Address5, OutScript as OutScript5, Transaction as Transaction8 } from "@scure/btc-signer";
7021
+ import { Address as Address4, OutScript as OutScript4, Transaction as Transaction8 } from "@scure/btc-signer";
6422
7022
  import { Mutex } from "async-mutex";
6423
7023
  import { uuidv7 as uuidv75, uuidv7obj } from "uuidv7";
6424
7024
 
@@ -6569,10 +7169,18 @@ var CompleteCoopExit = `
6569
7169
  var CompleteLeavesSwap = `
6570
7170
  mutation CompleteLeavesSwap(
6571
7171
  $adaptor_secret_key: String!
7172
+ $direct_adaptor_secret_key: String!
7173
+ $direct_from_cpfp_adaptor_secret_key: String!
6572
7174
  $user_outbound_transfer_external_id: UUID!
6573
7175
  $leaves_swap_request_id: ID!
6574
7176
  ) {
6575
- complete_leaves_swap(input: { adaptor_secret_key: $adaptor_secret_key, user_outbound_transfer_external_id: $user_outbound_transfer_external_id, leaves_swap_request_id: $leaves_swap_request_id }) {
7177
+ complete_leaves_swap(input: {
7178
+ adaptor_secret_key: $adaptor_secret_key,
7179
+ direct_adaptor_secret_key: $direct_adaptor_secret_key,
7180
+ direct_from_cpfp_adaptor_secret_key: $direct_from_cpfp_adaptor_secret_key,
7181
+ user_outbound_transfer_external_id: $user_outbound_transfer_external_id,
7182
+ leaves_swap_request_id: $leaves_swap_request_id
7183
+ }) {
6576
7184
  request {
6577
7185
  ...LeavesSwapRequestFragment
6578
7186
  }
@@ -6695,6 +7303,8 @@ var RequestLightningSend = `
6695
7303
  var RequestSwapLeaves = `
6696
7304
  mutation RequestSwapLeaves(
6697
7305
  $adaptor_pubkey: PublicKey!
7306
+ $direct_adaptor_pubkey: PublicKey
7307
+ $direct_from_cpfp_adaptor_pubkey: PublicKey
6698
7308
  $total_amount_sats: Long!
6699
7309
  $target_amount_sats: Long!
6700
7310
  $fee_sats: Long!
@@ -6704,6 +7314,8 @@ var RequestSwapLeaves = `
6704
7314
  ) {
6705
7315
  request_leaves_swap(input: {
6706
7316
  adaptor_pubkey: $adaptor_pubkey
7317
+ direct_adaptor_pubkey: $direct_adaptor_pubkey
7318
+ direct_from_cpfp_adaptor_pubkey: $direct_from_cpfp_adaptor_pubkey
6707
7319
  total_amount_sats: $total_amount_sats
6708
7320
  target_amount_sats: $target_amount_sats
6709
7321
  fee_sats: $fee_sats
@@ -6982,14 +7594,18 @@ var LightningSendFeeEstimate = `
6982
7594
  ${FRAGMENT13}
6983
7595
  `;
6984
7596
 
6985
- // src/graphql/queries/Transfer.ts
6986
- var GetTransfer = `
6987
- query Transfer($transfer_spark_id: UUID!) {
6988
- transfer(transfer_spark_id: $transfer_spark_id) {
7597
+ // src/graphql/queries/Transfers.ts
7598
+ var GetTransfers = `
7599
+ query Transfers($transfer_spark_ids: [UUID!]!) {
7600
+ transfers(transfer_spark_ids: $transfer_spark_ids) {
6989
7601
  ...TransferFragment
7602
+ transfer_user_request: user_request {
7603
+ ...UserRequestFragment
7604
+ }
6990
7605
  }
6991
7606
  }
6992
7607
  ${FRAGMENT}
7608
+ ${FRAGMENT6}
6993
7609
  `;
6994
7610
 
6995
7611
  // src/graphql/queries/UserRequest.ts
@@ -7194,6 +7810,8 @@ var SspClient = class {
7194
7810
  }
7195
7811
  async requestLeaveSwap({
7196
7812
  adaptorPubkey,
7813
+ directAdaptorPubkey,
7814
+ directFromCpfpAdaptorPubkey,
7197
7815
  totalAmountSats,
7198
7816
  targetAmountSats,
7199
7817
  feeSats,
@@ -7205,6 +7823,8 @@ var SspClient = class {
7205
7823
  queryPayload: RequestSwapLeaves,
7206
7824
  variables: {
7207
7825
  adaptor_pubkey: adaptorPubkey,
7826
+ direct_adaptor_pubkey: directAdaptorPubkey,
7827
+ direct_from_cpfp_adaptor_pubkey: directFromCpfpAdaptorPubkey,
7208
7828
  total_amount_sats: totalAmountSats,
7209
7829
  target_amount_sats: targetAmountSats,
7210
7830
  fee_sats: feeSats,
@@ -7223,6 +7843,8 @@ var SspClient = class {
7223
7843
  }
7224
7844
  async completeLeaveSwap({
7225
7845
  adaptorSecretKey,
7846
+ directAdaptorSecretKey,
7847
+ directFromCpfpAdaptorSecretKey,
7226
7848
  userOutboundTransferExternalId,
7227
7849
  leavesSwapRequestId
7228
7850
  }) {
@@ -7230,6 +7852,8 @@ var SspClient = class {
7230
7852
  queryPayload: CompleteLeavesSwap,
7231
7853
  variables: {
7232
7854
  adaptor_secret_key: adaptorSecretKey,
7855
+ direct_adaptor_secret_key: directAdaptorSecretKey,
7856
+ direct_from_cpfp_adaptor_secret_key: directFromCpfpAdaptorSecretKey,
7233
7857
  user_outbound_transfer_external_id: userOutboundTransferExternalId,
7234
7858
  leaves_swap_request_id: leavesSwapRequestId
7235
7859
  },
@@ -7337,14 +7961,47 @@ var SspClient = class {
7337
7961
  }
7338
7962
  });
7339
7963
  }
7340
- async getTransfer(id) {
7964
+ async getTransfers(ids) {
7341
7965
  return await this.executeRawQuery({
7342
- queryPayload: GetTransfer,
7966
+ queryPayload: GetTransfers,
7343
7967
  variables: {
7344
- transfer_spark_id: id
7968
+ transfer_spark_ids: ids
7345
7969
  },
7346
7970
  constructObject: (response) => {
7347
- return TransferFromJson(response.transfer);
7971
+ return response.transfers.map((transfer) => {
7972
+ const transferObj = TransferFromJson(
7973
+ transfer
7974
+ );
7975
+ switch (transfer.transfer_user_request.__typename) {
7976
+ case "ClaimStaticDeposit":
7977
+ transferObj.userRequest = ClaimStaticDepositFromJson(
7978
+ transfer.transfer_user_request
7979
+ );
7980
+ break;
7981
+ case "CoopExitRequest":
7982
+ transferObj.userRequest = CoopExitRequestFromJson(
7983
+ transfer.transfer_user_request
7984
+ );
7985
+ break;
7986
+ case "LeavesSwapRequest":
7987
+ transferObj.userRequest = LeavesSwapRequestFromJson(
7988
+ transfer.transfer_user_request
7989
+ );
7990
+ break;
7991
+ case "LightningReceiveRequest":
7992
+ transferObj.userRequest = LightningReceiveRequestFromJson(
7993
+ transfer.transfer_user_request
7994
+ );
7995
+ break;
7996
+ case "LightningSendRequest":
7997
+ transferObj.userRequest = LightningSendRequestFromJson(
7998
+ transfer.transfer_user_request
7999
+ );
8000
+ break;
8001
+ }
8002
+ const { userRequestId, ...rest } = transferObj;
8003
+ return rest;
8004
+ });
7348
8005
  }
7349
8006
  });
7350
8007
  }
@@ -7497,11 +8154,7 @@ import { sha256 as sha2569 } from "@noble/hashes/sha2";
7497
8154
  import { Transaction as Transaction5 } from "@scure/btc-signer";
7498
8155
  import * as ecies2 from "eciesjs";
7499
8156
  import { uuidv7 as uuidv72 } from "uuidv7";
7500
- var INITIAL_TIME_LOCK2 = 2e3;
7501
8157
  var DEFAULT_EXPIRY_TIME = 10 * 60 * 1e3;
7502
- function initialSequence() {
7503
- return 1 << 30 | INITIAL_TIME_LOCK2;
7504
- }
7505
8158
  function getSigningJobProto(signingJob) {
7506
8159
  return {
7507
8160
  signingPublicKey: signingJob.signingPublicKey,
@@ -7518,12 +8171,14 @@ var BaseTransferService = class {
7518
8171
  this.connectionManager = connectionManager;
7519
8172
  this.signingService = signingService;
7520
8173
  }
7521
- async sendTransferTweakKey(transfer, leaves, refundSignatureMap) {
8174
+ async sendTransferTweakKey(transfer, leaves, cpfpRefundSignatureMap, directRefundSignatureMap, directFromCpfpRefundSignatureMap) {
7522
8175
  const keyTweakInputMap = await this.prepareSendTransferKeyTweaks(
7523
8176
  transfer.id,
7524
8177
  transfer.receiverIdentityPublicKey,
7525
8178
  leaves,
7526
- refundSignatureMap
8179
+ cpfpRefundSignatureMap,
8180
+ directRefundSignatureMap,
8181
+ directFromCpfpRefundSignatureMap
7527
8182
  );
7528
8183
  let updatedTransfer;
7529
8184
  const coordinatorOperator = this.config.getSigningOperators()[this.config.getCoordinatorIdentifier()];
@@ -7561,13 +8216,26 @@ var BaseTransferService = class {
7561
8216
  }
7562
8217
  return updatedTransfer;
7563
8218
  }
7564
- async deliverTransferPackage(transfer, leaves, refundSignatureMap) {
8219
+ async deliverTransferPackage(transfer, leaves, cpfpRefundSignatureMap, directRefundSignatureMap, directFromCpfpRefundSignatureMap) {
7565
8220
  const keyTweakInputMap = await this.prepareSendTransferKeyTweaks(
7566
8221
  transfer.id,
7567
8222
  transfer.receiverIdentityPublicKey,
7568
8223
  leaves,
7569
- refundSignatureMap
8224
+ cpfpRefundSignatureMap,
8225
+ directRefundSignatureMap,
8226
+ directFromCpfpRefundSignatureMap
7570
8227
  );
8228
+ for (const [key, operator] of Object.entries(
8229
+ this.config.getSigningOperators()
8230
+ )) {
8231
+ const tweaks = keyTweakInputMap.get(key);
8232
+ if (!tweaks) {
8233
+ throw new ValidationError("No tweaks for operator", {
8234
+ field: "operator",
8235
+ value: key
8236
+ });
8237
+ }
8238
+ }
7571
8239
  const transferPackage = await this.prepareTransferPackage(
7572
8240
  transfer.id,
7573
8241
  keyTweakInputMap,
@@ -7593,6 +8261,8 @@ var BaseTransferService = class {
7593
8261
  transferID,
7594
8262
  receiverIdentityPubkey,
7595
8263
  leaves,
8264
+ /* @__PURE__ */ new Map(),
8265
+ /* @__PURE__ */ new Map(),
7596
8266
  /* @__PURE__ */ new Map()
7597
8267
  );
7598
8268
  const transferPackage = await this.prepareTransferPackage(
@@ -7606,7 +8276,7 @@ var BaseTransferService = class {
7606
8276
  );
7607
8277
  let response;
7608
8278
  try {
7609
- response = await sparkClient.start_transfer({
8279
+ response = await sparkClient.start_transfer_v2({
7610
8280
  transferId: transferID,
7611
8281
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
7612
8282
  receiverIdentityPublicKey: receiverIdentityPubkey,
@@ -7635,12 +8305,22 @@ var BaseTransferService = class {
7635
8305
  nodes.push(leaf.leaf.id);
7636
8306
  }
7637
8307
  const signingCommitments = await sparkClient.get_signing_commitments({
7638
- nodeIds: nodes
8308
+ nodeIds: nodes,
8309
+ count: 3
7639
8310
  });
7640
- const leafSigningJobs = await this.signingService.signRefunds(
8311
+ const {
8312
+ cpfpLeafSigningJobs,
8313
+ directLeafSigningJobs,
8314
+ directFromCpfpLeafSigningJobs
8315
+ } = await this.signingService.signRefunds(
7641
8316
  leaves,
7642
- signingCommitments.signingCommitments,
7643
- receiverIdentityPubkey
8317
+ receiverIdentityPubkey,
8318
+ signingCommitments.signingCommitments.slice(0, leaves.length),
8319
+ signingCommitments.signingCommitments.slice(
8320
+ leaves.length,
8321
+ 2 * leaves.length
8322
+ ),
8323
+ signingCommitments.signingCommitments.slice(2 * leaves.length)
7644
8324
  );
7645
8325
  const encryptedKeyTweaks = {};
7646
8326
  for (const [key, value] of keyTweakInputMap) {
@@ -7660,12 +8340,11 @@ var BaseTransferService = class {
7660
8340
  encryptedKeyTweaks[key] = Uint8Array.from(encryptedProto);
7661
8341
  }
7662
8342
  const transferPackage = {
7663
- leavesToSend: leafSigningJobs,
8343
+ leavesToSend: cpfpLeafSigningJobs,
7664
8344
  keyTweakPackage: encryptedKeyTweaks,
7665
8345
  userSignature: new Uint8Array(),
7666
- // TODO: Add direct refund signature
7667
- directLeavesToSend: [],
7668
- directFromCpfpLeavesToSend: []
8346
+ directLeavesToSend: directLeafSigningJobs,
8347
+ directFromCpfpLeavesToSend: directFromCpfpLeafSigningJobs
7669
8348
  };
7670
8349
  const transferPackageSigningPayload = getTransferPackageSigningPayload(
7671
8350
  transferID,
@@ -7725,7 +8404,7 @@ var BaseTransferService = class {
7725
8404
  }
7726
8405
  return updatedTransfer;
7727
8406
  }
7728
- async signRefunds(leafDataMap, operatorSigningResults, adaptorPubKey) {
8407
+ async signRefunds(leafDataMap, operatorSigningResults, cpfpAdaptorPubKey, directAdaptorPubKey, directFromCpfpAdaptorPubKey) {
7729
8408
  const nodeSignatures = [];
7730
8409
  for (const operatorSigningResult of operatorSigningResults) {
7731
8410
  const leafData = leafDataMap.get(operatorSigningResult.leafId);
@@ -7740,54 +8419,120 @@ var BaseTransferService = class {
7740
8419
  `Output not found for leaf ${operatorSigningResult.leafId}`
7741
8420
  );
7742
8421
  }
7743
- const refundTxSighash = getSigHashFromTx(leafData.refundTx, 0, txOutput);
8422
+ const cpfpRefundTxSighash = getSigHashFromTx(
8423
+ leafData.refundTx,
8424
+ 0,
8425
+ txOutput
8426
+ );
7744
8427
  const publicKey = await this.config.signer.getPublicKeyFromDerivation(
7745
8428
  leafData.keyDerivation
7746
8429
  );
7747
- const userSignature = await this.config.signer.signFrost({
7748
- message: refundTxSighash,
8430
+ const cpfpUserSignature = await this.config.signer.signFrost({
8431
+ message: cpfpRefundTxSighash,
7749
8432
  publicKey,
7750
8433
  keyDerivation: leafData.keyDerivation,
7751
8434
  selfCommitment: leafData.signingNonceCommitment,
7752
8435
  statechainCommitments: operatorSigningResult.refundTxSigningResult?.signingNonceCommitments,
7753
- adaptorPubKey,
8436
+ adaptorPubKey: cpfpAdaptorPubKey,
7754
8437
  verifyingKey: operatorSigningResult.verifyingKey
7755
8438
  });
7756
- const refundAggregate = await this.config.signer.aggregateFrost({
7757
- message: refundTxSighash,
8439
+ const cpfpRefundAggregate = await this.config.signer.aggregateFrost({
8440
+ message: cpfpRefundTxSighash,
7758
8441
  statechainSignatures: operatorSigningResult.refundTxSigningResult?.signatureShares,
7759
8442
  statechainPublicKeys: operatorSigningResult.refundTxSigningResult?.publicKeys,
7760
8443
  verifyingKey: operatorSigningResult.verifyingKey,
7761
8444
  statechainCommitments: operatorSigningResult.refundTxSigningResult?.signingNonceCommitments,
7762
8445
  selfCommitment: leafData.signingNonceCommitment,
7763
8446
  publicKey,
7764
- selfSignature: userSignature,
7765
- adaptorPubKey
7766
- });
8447
+ selfSignature: cpfpUserSignature,
8448
+ adaptorPubKey: cpfpAdaptorPubKey
8449
+ });
8450
+ let directRefundAggregate;
8451
+ let directFromCpfpRefundAggregate;
8452
+ if (leafData.directTx) {
8453
+ const directTxOutput = leafData.directTx.getOutput(0);
8454
+ if (leafData.directRefundTx) {
8455
+ const directRefundTxSighash = getSigHashFromTx(
8456
+ leafData.directRefundTx,
8457
+ 0,
8458
+ directTxOutput
8459
+ );
8460
+ const directUserSignature = await this.config.signer.signFrost({
8461
+ message: directRefundTxSighash,
8462
+ publicKey,
8463
+ keyDerivation: leafData.keyDerivation,
8464
+ selfCommitment: leafData.directSigningNonceCommitment,
8465
+ statechainCommitments: operatorSigningResult.directRefundTxSigningResult?.signingNonceCommitments,
8466
+ adaptorPubKey: directAdaptorPubKey,
8467
+ verifyingKey: operatorSigningResult.verifyingKey
8468
+ });
8469
+ directRefundAggregate = await this.config.signer.aggregateFrost({
8470
+ message: directRefundTxSighash,
8471
+ statechainSignatures: operatorSigningResult.directRefundTxSigningResult?.signatureShares,
8472
+ statechainPublicKeys: operatorSigningResult.directRefundTxSigningResult?.publicKeys,
8473
+ verifyingKey: operatorSigningResult.verifyingKey,
8474
+ statechainCommitments: operatorSigningResult.directRefundTxSigningResult?.signingNonceCommitments,
8475
+ selfCommitment: leafData.directSigningNonceCommitment,
8476
+ publicKey,
8477
+ selfSignature: directUserSignature,
8478
+ adaptorPubKey: directAdaptorPubKey
8479
+ });
8480
+ }
8481
+ if (leafData.directFromCpfpRefundTx) {
8482
+ const directFromCpfpRefundTxSighash = getSigHashFromTx(
8483
+ leafData.directFromCpfpRefundTx,
8484
+ 0,
8485
+ txOutput
8486
+ );
8487
+ const directFromCpfpUserSignature = await this.config.signer.signFrost({
8488
+ message: directFromCpfpRefundTxSighash,
8489
+ publicKey,
8490
+ keyDerivation: leafData.keyDerivation,
8491
+ selfCommitment: leafData.directFromCpfpRefundSigningNonceCommitment,
8492
+ statechainCommitments: operatorSigningResult.directFromCpfpRefundTxSigningResult?.signingNonceCommitments,
8493
+ adaptorPubKey: directFromCpfpAdaptorPubKey,
8494
+ verifyingKey: operatorSigningResult.verifyingKey
8495
+ });
8496
+ directFromCpfpRefundAggregate = await this.config.signer.aggregateFrost({
8497
+ message: directFromCpfpRefundTxSighash,
8498
+ statechainSignatures: operatorSigningResult.directFromCpfpRefundTxSigningResult?.signatureShares,
8499
+ statechainPublicKeys: operatorSigningResult.directFromCpfpRefundTxSigningResult?.publicKeys,
8500
+ verifyingKey: operatorSigningResult.verifyingKey,
8501
+ statechainCommitments: operatorSigningResult.directFromCpfpRefundTxSigningResult?.signingNonceCommitments,
8502
+ selfCommitment: leafData.directFromCpfpRefundSigningNonceCommitment,
8503
+ publicKey,
8504
+ selfSignature: directFromCpfpUserSignature,
8505
+ adaptorPubKey: directFromCpfpAdaptorPubKey
8506
+ });
8507
+ }
8508
+ }
7767
8509
  nodeSignatures.push({
7768
8510
  nodeId: operatorSigningResult.leafId,
7769
- refundTxSignature: refundAggregate,
7770
8511
  nodeTxSignature: new Uint8Array(),
7771
- // TODO: Add direct refund signature
7772
8512
  directNodeTxSignature: new Uint8Array(),
7773
- directRefundTxSignature: new Uint8Array(),
7774
- directFromCpfpRefundTxSignature: new Uint8Array()
8513
+ refundTxSignature: cpfpRefundAggregate,
8514
+ directRefundTxSignature: directRefundAggregate ?? new Uint8Array(),
8515
+ directFromCpfpRefundTxSignature: directFromCpfpRefundAggregate ?? new Uint8Array()
7775
8516
  });
7776
8517
  }
7777
8518
  return nodeSignatures;
7778
8519
  }
7779
- async prepareSendTransferKeyTweaks(transferID, receiverIdentityPubkey, leaves, refundSignatureMap) {
8520
+ async prepareSendTransferKeyTweaks(transferID, receiverIdentityPubkey, leaves, cpfpRefundSignatureMap, directRefundSignatureMap, directFromCpfpRefundSignatureMap) {
7780
8521
  const receiverEciesPubKey = ecies2.PublicKey.fromHex(
7781
8522
  bytesToHex10(receiverIdentityPubkey)
7782
8523
  );
7783
8524
  const leavesTweaksMap = /* @__PURE__ */ new Map();
7784
8525
  for (const leaf of leaves) {
7785
- const refundSignature = refundSignatureMap.get(leaf.leaf.id);
8526
+ const cpfpRefundSignature = cpfpRefundSignatureMap.get(leaf.leaf.id);
8527
+ const directRefundSignature = directRefundSignatureMap.get(leaf.leaf.id);
8528
+ const directFromCpfpRefundSignature = directFromCpfpRefundSignatureMap.get(leaf.leaf.id);
7786
8529
  const leafTweaksMap = await this.prepareSingleSendTransferKeyTweak(
7787
8530
  transferID,
7788
8531
  leaf,
7789
8532
  receiverEciesPubKey,
7790
- refundSignature
8533
+ cpfpRefundSignature,
8534
+ directRefundSignature,
8535
+ directFromCpfpRefundSignature
7791
8536
  );
7792
8537
  for (const [identifier, leafTweak] of leafTweaksMap) {
7793
8538
  leavesTweaksMap.set(identifier, [
@@ -7798,7 +8543,7 @@ var BaseTransferService = class {
7798
8543
  }
7799
8544
  return leavesTweaksMap;
7800
8545
  }
7801
- async prepareSingleSendTransferKeyTweak(transferID, leaf, receiverEciesPubKey, refundSignature) {
8546
+ async prepareSingleSendTransferKeyTweak(transferID, leaf, receiverEciesPubKey, cpfpRefundSignature, directRefundSignature, directFromCpfpRefundSignature) {
7802
8547
  const signingOperators = this.config.getSigningOperators();
7803
8548
  const { shares, secretCipher } = await this.config.signer.subtractSplitAndEncrypt({
7804
8549
  first: leaf.keyDerivation,
@@ -7846,10 +8591,9 @@ var BaseTransferService = class {
7846
8591
  pubkeySharesTweak: Object.fromEntries(pubkeySharesTweak),
7847
8592
  secretCipher,
7848
8593
  signature,
7849
- refundSignature: refundSignature ?? new Uint8Array(),
7850
- // TODO: Add direct refund signature
7851
- directRefundSignature: new Uint8Array(),
7852
- directFromCpfpRefundSignature: new Uint8Array()
8594
+ refundSignature: cpfpRefundSignature ?? new Uint8Array(),
8595
+ directRefundSignature: directRefundSignature ?? new Uint8Array(),
8596
+ directFromCpfpRefundSignature: directFromCpfpRefundSignature ?? new Uint8Array()
7853
8597
  });
7854
8598
  }
7855
8599
  return leafTweaksMap;
@@ -7879,7 +8623,12 @@ var TransferService = class extends BaseTransferService {
7879
8623
  * Deprecated in v0.1.32
7880
8624
  */
7881
8625
  async sendTransfer(leaves, receiverIdentityPubkey) {
7882
- const { transfer, signatureMap } = await this.sendTransferSignRefund(
8626
+ const {
8627
+ transfer,
8628
+ signatureMap,
8629
+ directSignatureMap,
8630
+ directFromCpfpSignatureMap
8631
+ } = await this.sendTransferSignRefund(
7883
8632
  leaves,
7884
8633
  receiverIdentityPubkey,
7885
8634
  new Date(Date.now() + DEFAULT_EXPIRY_TIME)
@@ -7887,7 +8636,9 @@ var TransferService = class extends BaseTransferService {
7887
8636
  const transferWithTweakedKeys = await this.sendTransferTweakKey(
7888
8637
  transfer,
7889
8638
  leaves,
7890
- signatureMap
8639
+ signatureMap,
8640
+ directSignatureMap,
8641
+ directFromCpfpSignatureMap
7891
8642
  );
7892
8643
  return transferWithTweakedKeys;
7893
8644
  }
@@ -7991,7 +8742,13 @@ var TransferService = class extends BaseTransferService {
7991
8742
  return transferResp.transfers[0];
7992
8743
  }
7993
8744
  async sendTransferSignRefund(leaves, receiverIdentityPubkey, expiryTime) {
7994
- const { transfer, signatureMap, leafDataMap } = await this.sendTransferSignRefundInternal(
8745
+ const {
8746
+ transfer,
8747
+ signatureMap,
8748
+ directSignatureMap,
8749
+ directFromCpfpSignatureMap,
8750
+ leafDataMap
8751
+ } = await this.sendTransferSignRefundInternal(
7995
8752
  leaves,
7996
8753
  receiverIdentityPubkey,
7997
8754
  expiryTime,
@@ -8000,11 +8757,19 @@ var TransferService = class extends BaseTransferService {
8000
8757
  return {
8001
8758
  transfer,
8002
8759
  signatureMap,
8760
+ directSignatureMap,
8761
+ directFromCpfpSignatureMap,
8003
8762
  leafDataMap
8004
8763
  };
8005
8764
  }
8006
8765
  async startSwapSignRefund(leaves, receiverIdentityPubkey, expiryTime) {
8007
- const { transfer, signatureMap, leafDataMap } = await this.sendTransferSignRefundInternal(
8766
+ const {
8767
+ transfer,
8768
+ signatureMap,
8769
+ directSignatureMap,
8770
+ directFromCpfpSignatureMap,
8771
+ leafDataMap
8772
+ } = await this.sendTransferSignRefundInternal(
8008
8773
  leaves,
8009
8774
  receiverIdentityPubkey,
8010
8775
  expiryTime,
@@ -8013,31 +8778,45 @@ var TransferService = class extends BaseTransferService {
8013
8778
  return {
8014
8779
  transfer,
8015
8780
  signatureMap,
8781
+ directSignatureMap,
8782
+ directFromCpfpSignatureMap,
8016
8783
  leafDataMap
8017
8784
  };
8018
8785
  }
8019
- async counterSwapSignRefund(leaves, receiverIdentityPubkey, expiryTime, adaptorPubKey) {
8786
+ async counterSwapSignRefund(leaves, receiverIdentityPubkey, expiryTime, cpfpAdaptorPubKey, directAdaptorPubKey, directFromCpfpAdaptorPubKey) {
8020
8787
  return this.sendTransferSignRefundInternal(
8021
8788
  leaves,
8022
8789
  receiverIdentityPubkey,
8023
8790
  expiryTime,
8024
8791
  true,
8025
- adaptorPubKey
8792
+ cpfpAdaptorPubKey,
8793
+ directAdaptorPubKey,
8794
+ directFromCpfpAdaptorPubKey
8026
8795
  );
8027
8796
  }
8028
- async sendTransferSignRefundInternal(leaves, receiverIdentityPubkey, expiryTime, forSwap, adaptorPubKey) {
8797
+ async sendTransferSignRefundInternal(leaves, receiverIdentityPubkey, expiryTime, forSwap, cpfpAdaptorPubKey, directAdaptorPubKey, directFromCpfpAdaptorPubKey) {
8029
8798
  const transferId = uuidv72();
8030
8799
  const leafDataMap = /* @__PURE__ */ new Map();
8031
8800
  for (const leaf of leaves) {
8032
8801
  const signingNonceCommitment = await this.config.signer.getRandomSigningCommitment();
8802
+ const directSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
8803
+ const directFromCpfpRefundSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
8033
8804
  const tx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
8034
8805
  const refundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
8806
+ const directTx = leaf.leaf.directTx.length > 0 ? getTxFromRawTxBytes(leaf.leaf.directTx) : void 0;
8807
+ const directRefundTx = leaf.leaf.directRefundTx.length > 0 ? getTxFromRawTxBytes(leaf.leaf.directRefundTx) : void 0;
8808
+ const directFromCpfpRefundTx = leaf.leaf.directFromCpfpRefundTx.length > 0 ? getTxFromRawTxBytes(leaf.leaf.directFromCpfpRefundTx) : void 0;
8035
8809
  leafDataMap.set(leaf.leaf.id, {
8036
8810
  keyDerivation: leaf.keyDerivation,
8037
8811
  receivingPubkey: receiverIdentityPubkey,
8038
8812
  signingNonceCommitment,
8813
+ directSigningNonceCommitment,
8039
8814
  tx,
8815
+ directTx,
8040
8816
  refundTx,
8817
+ directRefundTx,
8818
+ directFromCpfpRefundTx,
8819
+ directFromCpfpRefundSigningNonceCommitment,
8041
8820
  vout: leaf.leaf.vout
8042
8821
  });
8043
8822
  }
@@ -8050,8 +8829,8 @@ var TransferService = class extends BaseTransferService {
8050
8829
  );
8051
8830
  let response;
8052
8831
  try {
8053
- if (adaptorPubKey !== void 0) {
8054
- response = await sparkClient.counter_leaf_swap({
8832
+ if (cpfpAdaptorPubKey !== void 0 || directAdaptorPubKey !== void 0 || directFromCpfpAdaptorPubKey !== void 0) {
8833
+ response = await sparkClient.counter_leaf_swap_v2({
8055
8834
  transfer: {
8056
8835
  transferId,
8057
8836
  leavesToSend: signingJobs,
@@ -8060,10 +8839,12 @@ var TransferService = class extends BaseTransferService {
8060
8839
  expiryTime
8061
8840
  },
8062
8841
  swapId: uuidv72(),
8063
- adaptorPublicKey: adaptorPubKey || new Uint8Array()
8842
+ adaptorPublicKey: cpfpAdaptorPubKey,
8843
+ directAdaptorPublicKey: directAdaptorPubKey,
8844
+ directFromCpfpAdaptorPublicKey: directFromCpfpAdaptorPubKey
8064
8845
  });
8065
8846
  } else if (forSwap) {
8066
- response = await sparkClient.start_leaf_swap({
8847
+ response = await sparkClient.start_leaf_swap_v2({
8067
8848
  transferId,
8068
8849
  leavesToSend: signingJobs,
8069
8850
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
@@ -8071,7 +8852,7 @@ var TransferService = class extends BaseTransferService {
8071
8852
  expiryTime
8072
8853
  });
8073
8854
  } else {
8074
- response = await sparkClient.start_transfer({
8855
+ response = await sparkClient.start_transfer_v2({
8075
8856
  transferId,
8076
8857
  leavesToSend: signingJobs,
8077
8858
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
@@ -8088,15 +8869,29 @@ var TransferService = class extends BaseTransferService {
8088
8869
  const signatures = await this.signRefunds(
8089
8870
  leafDataMap,
8090
8871
  response.signingResults,
8091
- adaptorPubKey
8872
+ cpfpAdaptorPubKey,
8873
+ directAdaptorPubKey,
8874
+ directFromCpfpAdaptorPubKey
8092
8875
  );
8093
- const signatureMap = /* @__PURE__ */ new Map();
8876
+ const cpfpSignatureMap = /* @__PURE__ */ new Map();
8877
+ const directSignatureMap = /* @__PURE__ */ new Map();
8878
+ const directFromCpfpSignatureMap = /* @__PURE__ */ new Map();
8094
8879
  for (const signature of signatures) {
8095
- signatureMap.set(signature.nodeId, signature.refundTxSignature);
8880
+ cpfpSignatureMap.set(signature.nodeId, signature.refundTxSignature);
8881
+ directSignatureMap.set(
8882
+ signature.nodeId,
8883
+ signature.directRefundTxSignature
8884
+ );
8885
+ directFromCpfpSignatureMap.set(
8886
+ signature.nodeId,
8887
+ signature.directFromCpfpRefundTxSignature
8888
+ );
8096
8889
  }
8097
8890
  return {
8098
8891
  transfer: response.transfer,
8099
- signatureMap,
8892
+ signatureMap: cpfpSignatureMap,
8893
+ directSignatureMap,
8894
+ directFromCpfpSignatureMap,
8100
8895
  leafDataMap,
8101
8896
  signingResults: response.signingResults
8102
8897
  };
@@ -8109,38 +8904,68 @@ var TransferService = class extends BaseTransferService {
8109
8904
  throw new Error(`Leaf data not found for leaf ${leaf.leaf.id}`);
8110
8905
  }
8111
8906
  const nodeTx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
8112
- const nodeOutPoint = {
8907
+ const cpfpNodeOutPoint = {
8113
8908
  txid: hexToBytes8(getTxId(nodeTx)),
8114
8909
  index: 0
8115
8910
  };
8911
+ let directNodeTx;
8912
+ let directNodeOutPoint;
8913
+ if (leaf.leaf.directTx.length > 0) {
8914
+ directNodeTx = getTxFromRawTxBytes(leaf.leaf.directTx);
8915
+ directNodeOutPoint = {
8916
+ txid: hexToBytes8(getTxId(directNodeTx)),
8917
+ index: 0
8918
+ };
8919
+ }
8116
8920
  const currRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
8117
- const nextSequence = isForClaim ? getTransactionSequence(currRefundTx.getInput(0).sequence) : getNextTransactionSequence(currRefundTx.getInput(0).sequence).nextSequence;
8921
+ const sequence = currRefundTx.getInput(0).sequence;
8922
+ if (!sequence) {
8923
+ throw new ValidationError("Invalid refund transaction", {
8924
+ field: "sequence",
8925
+ value: currRefundTx.getInput(0),
8926
+ expected: "Non-null sequence"
8927
+ });
8928
+ }
8929
+ const { nextSequence, nextDirectSequence } = isForClaim ? getTransactionSequence(sequence) : getNextTransactionSequence(sequence);
8118
8930
  const amountSats = currRefundTx.getOutput(0).amount;
8119
8931
  if (amountSats === void 0) {
8120
8932
  throw new Error("Amount not found in signRefunds");
8121
8933
  }
8122
- const refundTx = createRefundTx(
8123
- nextSequence,
8124
- nodeOutPoint,
8934
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
8935
+ sequence: nextSequence,
8936
+ directSequence: nextDirectSequence,
8937
+ input: cpfpNodeOutPoint,
8938
+ directInput: directNodeOutPoint,
8125
8939
  amountSats,
8126
- refundSigningData.receivingPubkey,
8127
- this.config.getNetwork()
8940
+ receivingPubkey: refundSigningData.receivingPubkey,
8941
+ network: this.config.getNetwork()
8942
+ });
8943
+ refundSigningData.refundTx = cpfpRefundTx;
8944
+ refundSigningData.directRefundTx = directRefundTx;
8945
+ refundSigningData.directFromCpfpRefundTx = directFromCpfpRefundTx;
8946
+ const cpfpRefundNonceCommitmentProto = refundSigningData.signingNonceCommitment;
8947
+ const directRefundNonceCommitmentProto = refundSigningData.directSigningNonceCommitment;
8948
+ const directFromCpfpRefundNonceCommitmentProto = refundSigningData.directFromCpfpRefundSigningNonceCommitment;
8949
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(
8950
+ refundSigningData.keyDerivation
8128
8951
  );
8129
- refundSigningData.refundTx = refundTx;
8130
- const refundNonceCommitmentProto = refundSigningData.signingNonceCommitment;
8131
8952
  signingJobs.push({
8132
8953
  leafId: leaf.leaf.id,
8133
8954
  refundTxSigningJob: {
8134
- signingPublicKey: await this.config.signer.getPublicKeyFromDerivation(
8135
- refundSigningData.keyDerivation
8136
- ),
8137
- rawTx: refundTx.toBytes(),
8138
- signingNonceCommitment: refundNonceCommitmentProto.commitment
8955
+ signingPublicKey,
8956
+ rawTx: cpfpRefundTx.toBytes(),
8957
+ signingNonceCommitment: cpfpRefundNonceCommitmentProto.commitment
8139
8958
  },
8140
- // TODO: Add direct refund signature
8141
- directRefundTxSigningJob: void 0,
8142
- // TODO: Add direct refund signature
8143
- directFromCpfpRefundTxSigningJob: void 0
8959
+ directRefundTxSigningJob: directRefundTx ? {
8960
+ signingPublicKey,
8961
+ rawTx: directRefundTx.toBytes(),
8962
+ signingNonceCommitment: directRefundNonceCommitmentProto.commitment
8963
+ } : void 0,
8964
+ directFromCpfpRefundTxSigningJob: directFromCpfpRefundTx ? {
8965
+ signingPublicKey,
8966
+ rawTx: directFromCpfpRefundTx.toBytes(),
8967
+ signingNonceCommitment: directFromCpfpRefundNonceCommitmentProto.commitment
8968
+ } : void 0
8144
8969
  });
8145
8970
  }
8146
8971
  return signingJobs;
@@ -8261,13 +9086,17 @@ var TransferService = class extends BaseTransferService {
8261
9086
  const leafDataMap = /* @__PURE__ */ new Map();
8262
9087
  for (const leafKey of leafKeys) {
8263
9088
  const tx = getTxFromRawTxBytes(leafKey.leaf.nodeTx);
9089
+ const directTx = leafKey.leaf.directTx.length > 0 ? getTxFromRawTxBytes(leafKey.leaf.directTx) : void 0;
8264
9090
  leafDataMap.set(leafKey.leaf.id, {
8265
9091
  keyDerivation: leafKey.newKeyDerivation,
8266
9092
  receivingPubkey: await this.config.signer.getPublicKeyFromDerivation(
8267
9093
  leafKey.newKeyDerivation
8268
9094
  ),
8269
9095
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9096
+ directSigningNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9097
+ directFromCpfpRefundSigningNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
8270
9098
  tx,
9099
+ directTx,
8271
9100
  vout: leafKey.leaf.vout
8272
9101
  });
8273
9102
  }
@@ -8289,7 +9118,7 @@ var TransferService = class extends BaseTransferService {
8289
9118
  }
8290
9119
  }
8291
9120
  try {
8292
- resp = await sparkClient.claim_transfer_sign_refunds({
9121
+ resp = await sparkClient.claim_transfer_sign_refunds_v2({
8293
9122
  transferId: transfer.id,
8294
9123
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
8295
9124
  signingJobs
@@ -8304,7 +9133,7 @@ var TransferService = class extends BaseTransferService {
8304
9133
  this.config.getCoordinatorAddress()
8305
9134
  );
8306
9135
  try {
8307
- return await sparkClient.finalize_node_signatures({
9136
+ return await sparkClient.finalize_node_signatures_v2({
8308
9137
  intent: 1 /* TRANSFER */,
8309
9138
  nodeSignatures
8310
9139
  });
@@ -8343,109 +9172,134 @@ var TransferService = class extends BaseTransferService {
8343
9172
  throw new Error(`Error querying pending transfers by sender: ${error}`);
8344
9173
  }
8345
9174
  }
8346
- async refreshTimelockNodes(nodes, parentNode) {
8347
- if (nodes.length === 0) {
8348
- throw Error("no nodes to refresh");
8349
- }
9175
+ async refreshTimelockNodesInternal(node, parentNode, useTestUnilateralSequence) {
8350
9176
  const signingJobs = [];
8351
- const newNodeTxs = [];
8352
- for (let i = 0; i < nodes.length; i++) {
8353
- const node = nodes[i];
8354
- if (!node) {
8355
- throw Error("could not get node");
8356
- }
8357
- const nodeTx = getTxFromRawTxBytes(node?.nodeTx);
8358
- const input = nodeTx.getInput(0);
8359
- if (!input) {
8360
- throw Error("Could not fetch tx input");
8361
- }
8362
- const newTx = new Transaction5({ version: 3, allowUnknownOutputs: true });
8363
- const originalOutput = nodeTx.getOutput(0);
8364
- if (!originalOutput) {
8365
- throw Error("Could not get original output");
8366
- }
8367
- newTx.addOutput({
8368
- script: originalOutput.script,
8369
- amount: originalOutput.amount
8370
- });
8371
- for (let j = 1; j < nodeTx.outputsLength; j++) {
8372
- const additionalOutput = nodeTx.getOutput(j);
8373
- if (additionalOutput) {
8374
- newTx.addOutput(additionalOutput);
8375
- }
8376
- }
8377
- if (i === 0) {
8378
- const currSequence = input.sequence;
8379
- newTx.addInput({
8380
- ...input,
8381
- sequence: getNextTransactionSequence(currSequence).nextSequence
8382
- });
8383
- } else {
8384
- newTx.addInput({
8385
- ...input,
8386
- sequence: initialSequence(),
8387
- txid: newNodeTxs[i - 1]?.id
8388
- });
8389
- }
9177
+ const parentNodeTx = getTxFromRawTxBytes(parentNode.nodeTx);
9178
+ const parentNodeOutput = parentNodeTx.getOutput(0);
9179
+ if (!parentNodeOutput) {
9180
+ throw Error("Could not get parent node output");
9181
+ }
9182
+ const nodeTx = getTxFromRawTxBytes(node.nodeTx);
9183
+ const nodeInput = nodeTx.getInput(0);
9184
+ const nodeOutput = nodeTx.getOutput(0);
9185
+ if (!nodeOutput) {
9186
+ throw Error("Could not get node output");
9187
+ }
9188
+ let directNodeTx;
9189
+ let directNodeInput;
9190
+ if (node.directTx.length > 0) {
9191
+ directNodeTx = getTxFromRawTxBytes(node.directTx);
9192
+ directNodeInput = directNodeTx.getInput(0);
9193
+ }
9194
+ const currSequence = nodeInput.sequence;
9195
+ if (!currSequence) {
9196
+ throw new ValidationError("Invalid node transaction", {
9197
+ field: "sequence",
9198
+ value: nodeInput,
9199
+ expected: "Non-null sequence"
9200
+ });
9201
+ }
9202
+ let { nextSequence, nextDirectSequence } = getNextTransactionSequence(
9203
+ currSequence,
9204
+ true
9205
+ );
9206
+ const output = {
9207
+ script: parentNodeOutput.script,
9208
+ amount: parentNodeOutput.amount
9209
+ };
9210
+ const newNodeInput = {
9211
+ txid: nodeInput.txid,
9212
+ index: nodeInput.index,
9213
+ sequence: useTestUnilateralSequence ? TEST_UNILATERAL_SEQUENCE : nextSequence
9214
+ };
9215
+ const newDirectInput = directNodeTx && directNodeInput ? {
9216
+ txid: directNodeInput.txid,
9217
+ index: directNodeInput.index,
9218
+ sequence: useTestUnilateralSequence ? TEST_UNILATERAL_DIRECT_SEQUENCE : nextDirectSequence
9219
+ } : void 0;
9220
+ const { cpfpNodeTx, directNodeTx: newDirectNodeTx } = createNodeTxs(
9221
+ output,
9222
+ newNodeInput,
9223
+ newDirectInput
9224
+ );
9225
+ const newCpfpNodeOutput = cpfpNodeTx.getOutput(0);
9226
+ if (!newCpfpNodeOutput) {
9227
+ throw Error("Could not get new cpfp node output");
9228
+ }
9229
+ const newDirectNodeOutput = newDirectNodeTx?.getOutput(0);
9230
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation({
9231
+ type: "leaf" /* LEAF */,
9232
+ path: node.id
9233
+ });
9234
+ signingJobs.push({
9235
+ signingPublicKey,
9236
+ rawTx: cpfpNodeTx.toBytes(),
9237
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9238
+ type: "node",
9239
+ parentTxOut: parentNodeOutput
9240
+ });
9241
+ if (newDirectNodeTx) {
8390
9242
  signingJobs.push({
8391
- signingPublicKey: await this.config.signer.getPublicKeyFromDerivation({
8392
- type: "leaf" /* LEAF */,
8393
- path: node.id
8394
- }),
8395
- rawTx: newTx.toBytes(),
8396
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
9243
+ signingPublicKey,
9244
+ rawTx: newDirectNodeTx.toBytes(),
9245
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9246
+ type: "directNode",
9247
+ parentTxOut: parentNodeOutput
8397
9248
  });
8398
- newNodeTxs[i] = newTx;
8399
9249
  }
8400
- const leaf = nodes[nodes.length - 1];
8401
- if (!leaf?.refundTx) {
8402
- throw Error("leaf does not have refund tx");
9250
+ const newCpfpRefundOutPoint = {
9251
+ txid: hexToBytes8(getTxId(cpfpNodeTx)),
9252
+ index: 0
9253
+ };
9254
+ let newDirectRefundOutPoint;
9255
+ if (newDirectNodeTx) {
9256
+ newDirectRefundOutPoint = {
9257
+ txid: hexToBytes8(getTxId(newDirectNodeTx)),
9258
+ index: 0
9259
+ };
8403
9260
  }
8404
- const refundTx = getTxFromRawTxBytes(leaf?.refundTx);
8405
- const newRefundTx = new Transaction5({
8406
- version: 3,
8407
- allowUnknownOutputs: true
9261
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
9262
+ sequence: INITIAL_SEQUENCE,
9263
+ directSequence: INITIAL_DIRECT_SEQUENCE,
9264
+ input: newCpfpRefundOutPoint,
9265
+ directInput: newDirectRefundOutPoint,
9266
+ amountSats: nodeOutput.amount,
9267
+ receivingPubkey: await this.config.signer.getPublicKeyFromDerivation({
9268
+ type: "leaf" /* LEAF */,
9269
+ path: node.id
9270
+ }),
9271
+ network: this.config.getNetwork()
8408
9272
  });
8409
- const originalRefundOutput = refundTx.getOutput(0);
8410
- if (!originalRefundOutput) {
8411
- throw Error("Could not get original refund output");
8412
- }
8413
- newRefundTx.addOutput({
8414
- script: originalRefundOutput.script,
8415
- amount: originalRefundOutput.amount
9273
+ signingJobs.push({
9274
+ signingPublicKey,
9275
+ rawTx: cpfpRefundTx.toBytes(),
9276
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9277
+ type: "cpfp",
9278
+ parentTxOut: newCpfpNodeOutput
8416
9279
  });
8417
- for (let j = 1; j < refundTx.outputsLength; j++) {
8418
- const additionalOutput = refundTx.getOutput(j);
8419
- if (additionalOutput) {
8420
- newRefundTx.addOutput(additionalOutput);
8421
- }
8422
- }
8423
- const refundTxInput = refundTx.getInput(0);
8424
- if (!refundTxInput) {
8425
- throw Error("refund tx doesn't have input");
9280
+ if (directRefundTx && newDirectNodeOutput) {
9281
+ signingJobs.push({
9282
+ signingPublicKey,
9283
+ rawTx: directRefundTx.toBytes(),
9284
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9285
+ type: "direct",
9286
+ parentTxOut: newDirectNodeOutput
9287
+ });
8426
9288
  }
8427
- if (!newNodeTxs[newNodeTxs.length - 1]) {
8428
- throw Error("Could not get last node tx");
9289
+ if (directFromCpfpRefundTx && newCpfpNodeOutput) {
9290
+ signingJobs.push({
9291
+ signingPublicKey,
9292
+ rawTx: directFromCpfpRefundTx.toBytes(),
9293
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9294
+ type: "directFromCpfp",
9295
+ parentTxOut: newCpfpNodeOutput
9296
+ });
8429
9297
  }
8430
- newRefundTx.addInput({
8431
- ...refundTxInput,
8432
- sequence: initialSequence(),
8433
- txid: getTxId(newNodeTxs[newNodeTxs.length - 1])
8434
- });
8435
- const refundSigningJob = {
8436
- signingPublicKey: await this.config.signer.getPublicKeyFromDerivation({
8437
- type: "leaf" /* LEAF */,
8438
- path: leaf.id
8439
- }),
8440
- rawTx: newRefundTx.toBytes(),
8441
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
8442
- };
8443
- signingJobs.push(refundSigningJob);
8444
9298
  const sparkClient = await this.connectionManager.createSparkClient(
8445
9299
  this.config.getCoordinatorAddress()
8446
9300
  );
8447
- const response = await sparkClient.refresh_timelock({
8448
- leafId: leaf.id,
9301
+ const response = await sparkClient.refresh_timelock_v2({
9302
+ leafId: node.id,
8449
9303
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
8450
9304
  signingJobs: signingJobs.map(getSigningJobProto)
8451
9305
  });
@@ -8455,45 +9309,27 @@ var TransferService = class extends BaseTransferService {
8455
9309
  );
8456
9310
  }
8457
9311
  let nodeSignatures = [];
8458
- let leafSignature;
8459
- let refundSignature;
8460
- let leafNodeId;
8461
- for (let i = 0; i < response.signingResults.length; i++) {
8462
- const signingResult = response.signingResults[i];
9312
+ let leafCpfpSignature;
9313
+ let leafDirectSignature;
9314
+ let cpfpRefundSignature;
9315
+ let directRefundSignature;
9316
+ let directFromCpfpRefundSignature;
9317
+ for (const [i, signingResult] of response.signingResults.entries()) {
8463
9318
  const signingJob = signingJobs[i];
8464
9319
  if (!signingJob || !signingResult) {
8465
9320
  throw Error("Signing job does not exist");
8466
9321
  }
8467
- if (!signingJob.signingNonceCommitment) {
8468
- throw Error("nonce commitment does not exist");
8469
- }
8470
9322
  const rawTx = getTxFromRawTxBytes(signingJob.rawTx);
8471
- let parentTx;
8472
- let nodeId;
8473
- let vout;
8474
- if (i === nodes.length) {
8475
- nodeId = nodes[i - 1]?.id;
8476
- parentTx = newNodeTxs[i - 1];
8477
- vout = 0;
8478
- } else if (i === 0) {
8479
- nodeId = nodes[i]?.id;
8480
- parentTx = getTxFromRawTxBytes(parentNode.nodeTx);
8481
- vout = nodes[i]?.vout;
8482
- } else {
8483
- nodeId = nodes[i]?.id;
8484
- parentTx = newNodeTxs[i - 1];
8485
- vout = nodes[i]?.vout;
9323
+ const txOut = signingJob.parentTxOut;
9324
+ if (!txOut) {
9325
+ throw Error("Could not get tx out");
8486
9326
  }
8487
- if (!parentTx || !nodeId || vout === void 0) {
8488
- throw Error("Could not parse signing results");
8489
- }
8490
- const txOut = parentTx.getOutput(vout);
8491
9327
  const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
8492
9328
  const userSignature = await this.config.signer.signFrost({
8493
9329
  message: rawTxSighash,
8494
9330
  keyDerivation: {
8495
9331
  type: "leaf" /* LEAF */,
8496
- path: nodeId
9332
+ path: node.id
8497
9333
  },
8498
9334
  publicKey: signingJob.signingPublicKey,
8499
9335
  verifyingKey: signingResult.verifyingKey,
@@ -8512,41 +9348,35 @@ var TransferService = class extends BaseTransferService {
8512
9348
  selfSignature: userSignature,
8513
9349
  adaptorPubKey: new Uint8Array()
8514
9350
  });
8515
- if (i !== nodes.length && i !== nodes.length - 1) {
8516
- nodeSignatures.push({
8517
- nodeId,
8518
- nodeTxSignature: signature,
8519
- refundTxSignature: new Uint8Array(),
8520
- // TODO: Add direct refund signature
8521
- directNodeTxSignature: new Uint8Array(),
8522
- directRefundTxSignature: new Uint8Array(),
8523
- directFromCpfpRefundTxSignature: new Uint8Array()
8524
- });
8525
- } else if (i === nodes.length) {
8526
- refundSignature = signature;
8527
- } else if (i === nodes.length - 1) {
8528
- leafNodeId = nodeId;
8529
- leafSignature = signature;
9351
+ if (signingJob.type === "node") {
9352
+ leafCpfpSignature = signature;
9353
+ } else if (signingJob.type === "directNode") {
9354
+ leafDirectSignature = signature;
9355
+ } else if (signingJob.type === "cpfp") {
9356
+ cpfpRefundSignature = signature;
9357
+ } else if (signingJob.type === "direct") {
9358
+ directRefundSignature = signature;
9359
+ } else if (signingJob.type === "directFromCpfp") {
9360
+ directFromCpfpRefundSignature = signature;
8530
9361
  }
8531
9362
  }
8532
- if (!leafSignature || !refundSignature || !leafNodeId) {
8533
- throw Error("leaf or refund signature does not exist");
8534
- }
8535
9363
  nodeSignatures.push({
8536
- nodeId: leafNodeId,
8537
- nodeTxSignature: leafSignature,
8538
- refundTxSignature: refundSignature,
8539
- // TODO: Add direct refund signature
8540
- directNodeTxSignature: new Uint8Array(),
8541
- directRefundTxSignature: new Uint8Array(),
8542
- directFromCpfpRefundTxSignature: new Uint8Array()
8543
- });
8544
- const result = await sparkClient.finalize_node_signatures({
9364
+ nodeId: node.id,
9365
+ nodeTxSignature: leafCpfpSignature || new Uint8Array(),
9366
+ directNodeTxSignature: leafDirectSignature || new Uint8Array(),
9367
+ refundTxSignature: cpfpRefundSignature || new Uint8Array(),
9368
+ directRefundTxSignature: directRefundSignature || new Uint8Array(),
9369
+ directFromCpfpRefundTxSignature: directFromCpfpRefundSignature || new Uint8Array()
9370
+ });
9371
+ const result = await sparkClient.finalize_node_signatures_v2({
8545
9372
  intent: 3 /* REFRESH */,
8546
9373
  nodeSignatures
8547
9374
  });
8548
9375
  return result;
8549
9376
  }
9377
+ async refreshTimelockNodes(node, parentNode) {
9378
+ return await this.refreshTimelockNodesInternal(node, parentNode);
9379
+ }
8550
9380
  async extendTimelock(node) {
8551
9381
  const nodeTx = getTxFromRawTxBytes(node.nodeTx);
8552
9382
  const refundTx = getTxFromRawTxBytes(node.refundTx);
@@ -8555,7 +9385,10 @@ var TransferService = class extends BaseTransferService {
8555
9385
  txid: hexToBytes8(getTxId(nodeTx)),
8556
9386
  index: 0
8557
9387
  };
8558
- const { nextSequence: newNodeSequence } = getNextTransactionSequence(refundSequence);
9388
+ const {
9389
+ nextSequence: newNodeSequence,
9390
+ nextDirectSequence: newDirectNodeSequence
9391
+ } = getNextTransactionSequence(refundSequence);
8559
9392
  const newNodeTx = new Transaction5({
8560
9393
  version: 3,
8561
9394
  allowUnknownOutputs: true
@@ -8568,81 +9401,122 @@ var TransferService = class extends BaseTransferService {
8568
9401
  newNodeTx.addOutput({
8569
9402
  script: originalOutput.script,
8570
9403
  amount: originalOutput.amount
8571
- // feeReducedAmount,
8572
9404
  });
8573
9405
  newNodeTx.addOutput(getEphemeralAnchorOutput());
8574
- const newRefundOutPoint = {
9406
+ let newDirectNodeTx;
9407
+ if (node.directTx.length > 0) {
9408
+ newDirectNodeTx = new Transaction5({
9409
+ version: 3,
9410
+ allowUnknownOutputs: true
9411
+ });
9412
+ newDirectNodeTx.addInput({
9413
+ ...newNodeOutPoint,
9414
+ sequence: newDirectNodeSequence
9415
+ });
9416
+ newDirectNodeTx.addOutput({
9417
+ script: originalOutput.script,
9418
+ amount: maybeApplyFee(originalOutput.amount)
9419
+ });
9420
+ }
9421
+ const newCpfpRefundOutPoint = {
8575
9422
  txid: hexToBytes8(getTxId(newNodeTx)),
8576
9423
  index: 0
8577
9424
  };
9425
+ let newDirectRefundOutPoint;
9426
+ if (newDirectNodeTx) {
9427
+ newDirectRefundOutPoint = {
9428
+ txid: hexToBytes8(getTxId(newDirectNodeTx)),
9429
+ index: 0
9430
+ };
9431
+ }
8578
9432
  const amountSats = refundTx.getOutput(0).amount;
8579
9433
  if (amountSats === void 0) {
8580
9434
  throw new Error("Amount not found in extendTimelock");
8581
9435
  }
8582
- const signingPubKey = await this.config.signer.getPublicKeyFromDerivation({
9436
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation({
8583
9437
  type: "leaf" /* LEAF */,
8584
9438
  path: node.id
8585
9439
  });
8586
- const newRefundTx = createRefundTx(
8587
- initialSequence(),
8588
- newRefundOutPoint,
9440
+ const {
9441
+ cpfpRefundTx: newCpfpRefundTx,
9442
+ directRefundTx: newDirectRefundTx,
9443
+ directFromCpfpRefundTx: newDirectFromCpfpRefundTx
9444
+ } = createRefundTxs({
9445
+ sequence: INITIAL_SEQUENCE,
9446
+ directSequence: INITIAL_DIRECT_SEQUENCE,
9447
+ input: newCpfpRefundOutPoint,
9448
+ directInput: newDirectRefundOutPoint,
8589
9449
  amountSats,
8590
- // feeReducedRefundAmount,
8591
- signingPubKey,
8592
- this.config.getNetwork()
8593
- );
9450
+ receivingPubkey: signingPublicKey,
9451
+ network: this.config.getNetwork()
9452
+ });
9453
+ if (!newCpfpRefundTx) {
9454
+ throw new ValidationError(
9455
+ "Failed to create refund transactions in extendTimelock"
9456
+ );
9457
+ }
8594
9458
  const nodeSighash = getSigHashFromTx(newNodeTx, 0, nodeTx.getOutput(0));
8595
- const refundSighash = getSigHashFromTx(
8596
- newRefundTx,
9459
+ const directNodeSighash = newDirectNodeTx ? getSigHashFromTx(newDirectNodeTx, 0, nodeTx.getOutput(0)) : void 0;
9460
+ const cpfpRefundSighash = getSigHashFromTx(
9461
+ newCpfpRefundTx,
8597
9462
  0,
8598
9463
  newNodeTx.getOutput(0)
8599
9464
  );
9465
+ const directRefundSighash = newDirectNodeTx && newDirectRefundTx ? getSigHashFromTx(newDirectRefundTx, 0, newDirectNodeTx.getOutput(0)) : void 0;
9466
+ const directFromCpfpRefundSighash = newDirectFromCpfpRefundTx ? getSigHashFromTx(newDirectFromCpfpRefundTx, 0, newNodeTx.getOutput(0)) : void 0;
8600
9467
  const newNodeSigningJob = {
8601
- signingPublicKey: signingPubKey,
9468
+ signingPublicKey,
8602
9469
  rawTx: newNodeTx.toBytes(),
8603
9470
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
8604
9471
  };
8605
- const newRefundSigningJob = {
8606
- signingPublicKey: signingPubKey,
8607
- rawTx: newRefundTx.toBytes(),
9472
+ const newDirectNodeSigningJob = newDirectNodeTx ? {
9473
+ signingPublicKey,
9474
+ rawTx: newDirectNodeTx.toBytes(),
9475
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
9476
+ } : void 0;
9477
+ const newCpfpRefundSigningJob = {
9478
+ signingPublicKey,
9479
+ rawTx: newCpfpRefundTx.toBytes(),
8608
9480
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
8609
9481
  };
9482
+ const newDirectRefundSigningJob = newDirectRefundTx ? {
9483
+ signingPublicKey,
9484
+ rawTx: newDirectRefundTx.toBytes(),
9485
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
9486
+ } : void 0;
9487
+ const newDirectFromCpfpRefundSigningJob = newDirectFromCpfpRefundTx ? {
9488
+ signingPublicKey,
9489
+ rawTx: newDirectFromCpfpRefundTx.toBytes(),
9490
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
9491
+ } : void 0;
8610
9492
  const sparkClient = await this.connectionManager.createSparkClient(
8611
9493
  this.config.getCoordinatorAddress()
8612
9494
  );
8613
- const response = await sparkClient.extend_leaf({
9495
+ const response = await sparkClient.extend_leaf_v2({
8614
9496
  leafId: node.id,
8615
9497
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
8616
9498
  nodeTxSigningJob: getSigningJobProto(newNodeSigningJob),
8617
- refundTxSigningJob: getSigningJobProto(newRefundSigningJob)
9499
+ directNodeTxSigningJob: newDirectNodeSigningJob ? getSigningJobProto(newDirectNodeSigningJob) : void 0,
9500
+ refundTxSigningJob: getSigningJobProto(newCpfpRefundSigningJob),
9501
+ directRefundTxSigningJob: newDirectRefundSigningJob ? getSigningJobProto(newDirectRefundSigningJob) : void 0,
9502
+ directFromCpfpRefundTxSigningJob: newDirectFromCpfpRefundSigningJob ? getSigningJobProto(newDirectFromCpfpRefundSigningJob) : void 0
8618
9503
  });
8619
9504
  if (!response.nodeTxSigningResult || !response.refundTxSigningResult) {
8620
9505
  throw new Error("Signing result does not exist");
8621
9506
  }
9507
+ const keyDerivation = {
9508
+ type: "leaf" /* LEAF */,
9509
+ path: node.id
9510
+ };
8622
9511
  const nodeUserSig = await this.config.signer.signFrost({
8623
9512
  message: nodeSighash,
8624
- keyDerivation: {
8625
- type: "leaf" /* LEAF */,
8626
- path: node.id
8627
- },
8628
- publicKey: signingPubKey,
9513
+ keyDerivation,
9514
+ publicKey: signingPublicKey,
8629
9515
  verifyingKey: response.nodeTxSigningResult.verifyingKey,
8630
9516
  selfCommitment: newNodeSigningJob.signingNonceCommitment,
8631
9517
  statechainCommitments: response.nodeTxSigningResult.signingResult?.signingNonceCommitments,
8632
9518
  adaptorPubKey: new Uint8Array()
8633
9519
  });
8634
- const refundUserSig = await this.config.signer.signFrost({
8635
- message: refundSighash,
8636
- keyDerivation: {
8637
- type: "leaf" /* LEAF */,
8638
- path: node.id
8639
- },
8640
- publicKey: signingPubKey,
8641
- verifyingKey: response.refundTxSigningResult.verifyingKey,
8642
- selfCommitment: newRefundSigningJob.signingNonceCommitment,
8643
- statechainCommitments: response.refundTxSigningResult.signingResult?.signingNonceCommitments,
8644
- adaptorPubKey: new Uint8Array()
8645
- });
8646
9520
  const nodeSig = await this.config.signer.aggregateFrost({
8647
9521
  message: nodeSighash,
8648
9522
  statechainSignatures: response.nodeTxSigningResult.signingResult?.signatureShares,
@@ -8650,122 +9524,253 @@ var TransferService = class extends BaseTransferService {
8650
9524
  verifyingKey: response.nodeTxSigningResult.verifyingKey,
8651
9525
  statechainCommitments: response.nodeTxSigningResult.signingResult?.signingNonceCommitments,
8652
9526
  selfCommitment: newNodeSigningJob.signingNonceCommitment,
8653
- publicKey: signingPubKey,
9527
+ publicKey: signingPublicKey,
8654
9528
  selfSignature: nodeUserSig,
8655
9529
  adaptorPubKey: new Uint8Array()
8656
9530
  });
8657
- const refundSig = await this.config.signer.aggregateFrost({
8658
- message: refundSighash,
9531
+ let directNodeSig;
9532
+ if (directNodeSighash && newDirectNodeSigningJob && response.directNodeTxSigningResult) {
9533
+ const directNodeUserSig = await this.config.signer.signFrost({
9534
+ message: directNodeSighash,
9535
+ keyDerivation,
9536
+ publicKey: signingPublicKey,
9537
+ verifyingKey: response.directNodeTxSigningResult.verifyingKey,
9538
+ selfCommitment: newDirectNodeSigningJob.signingNonceCommitment,
9539
+ statechainCommitments: response.directNodeTxSigningResult.signingResult?.signingNonceCommitments,
9540
+ adaptorPubKey: new Uint8Array()
9541
+ });
9542
+ directNodeSig = await this.config.signer.aggregateFrost({
9543
+ message: directNodeSighash,
9544
+ statechainSignatures: response.directNodeTxSigningResult.signingResult?.signatureShares,
9545
+ statechainPublicKeys: response.directNodeTxSigningResult.signingResult?.publicKeys,
9546
+ verifyingKey: response.directNodeTxSigningResult.verifyingKey,
9547
+ statechainCommitments: response.directNodeTxSigningResult.signingResult?.signingNonceCommitments,
9548
+ selfCommitment: newDirectNodeSigningJob.signingNonceCommitment,
9549
+ publicKey: signingPublicKey,
9550
+ selfSignature: directNodeUserSig,
9551
+ adaptorPubKey: new Uint8Array()
9552
+ });
9553
+ }
9554
+ const cpfpRefundUserSig = await this.config.signer.signFrost({
9555
+ message: cpfpRefundSighash,
9556
+ keyDerivation,
9557
+ publicKey: signingPublicKey,
9558
+ verifyingKey: response.refundTxSigningResult.verifyingKey,
9559
+ selfCommitment: newCpfpRefundSigningJob.signingNonceCommitment,
9560
+ statechainCommitments: response.refundTxSigningResult.signingResult?.signingNonceCommitments,
9561
+ adaptorPubKey: new Uint8Array()
9562
+ });
9563
+ const cpfpRefundSig = await this.config.signer.aggregateFrost({
9564
+ message: cpfpRefundSighash,
8659
9565
  statechainSignatures: response.refundTxSigningResult.signingResult?.signatureShares,
8660
9566
  statechainPublicKeys: response.refundTxSigningResult.signingResult?.publicKeys,
8661
9567
  verifyingKey: response.refundTxSigningResult.verifyingKey,
8662
9568
  statechainCommitments: response.refundTxSigningResult.signingResult?.signingNonceCommitments,
8663
- selfCommitment: newRefundSigningJob.signingNonceCommitment,
8664
- publicKey: signingPubKey,
8665
- selfSignature: refundUserSig,
9569
+ selfCommitment: newCpfpRefundSigningJob.signingNonceCommitment,
9570
+ publicKey: signingPublicKey,
9571
+ selfSignature: cpfpRefundUserSig,
8666
9572
  adaptorPubKey: new Uint8Array()
8667
9573
  });
8668
- return await sparkClient.finalize_node_signatures({
9574
+ let directRefundSig;
9575
+ if (directRefundSighash && newDirectRefundSigningJob && response.directRefundTxSigningResult) {
9576
+ const directRefundUserSig = await this.config.signer.signFrost({
9577
+ message: directRefundSighash,
9578
+ keyDerivation,
9579
+ publicKey: signingPublicKey,
9580
+ verifyingKey: response.directRefundTxSigningResult.verifyingKey,
9581
+ selfCommitment: newDirectRefundSigningJob.signingNonceCommitment,
9582
+ statechainCommitments: response.directRefundTxSigningResult.signingResult?.signingNonceCommitments,
9583
+ adaptorPubKey: new Uint8Array()
9584
+ });
9585
+ directRefundSig = await this.config.signer.aggregateFrost({
9586
+ message: directRefundSighash,
9587
+ statechainSignatures: response.directRefundTxSigningResult.signingResult?.signatureShares,
9588
+ statechainPublicKeys: response.directRefundTxSigningResult.signingResult?.publicKeys,
9589
+ verifyingKey: response.directRefundTxSigningResult.verifyingKey,
9590
+ statechainCommitments: response.directRefundTxSigningResult.signingResult?.signingNonceCommitments,
9591
+ selfCommitment: newDirectRefundSigningJob.signingNonceCommitment,
9592
+ publicKey: signingPublicKey,
9593
+ selfSignature: directRefundUserSig,
9594
+ adaptorPubKey: new Uint8Array()
9595
+ });
9596
+ }
9597
+ let directFromCpfpRefundSig;
9598
+ if (directFromCpfpRefundSighash && newDirectFromCpfpRefundSigningJob && response.directFromCpfpRefundTxSigningResult) {
9599
+ const directFromCpfpRefundUserSig = await this.config.signer.signFrost({
9600
+ message: directFromCpfpRefundSighash,
9601
+ keyDerivation,
9602
+ publicKey: signingPublicKey,
9603
+ verifyingKey: response.directFromCpfpRefundTxSigningResult.verifyingKey,
9604
+ selfCommitment: newDirectFromCpfpRefundSigningJob.signingNonceCommitment,
9605
+ statechainCommitments: response.directFromCpfpRefundTxSigningResult.signingResult?.signingNonceCommitments,
9606
+ adaptorPubKey: new Uint8Array()
9607
+ });
9608
+ directFromCpfpRefundSig = await this.config.signer.aggregateFrost({
9609
+ message: directFromCpfpRefundSighash,
9610
+ statechainSignatures: response.directFromCpfpRefundTxSigningResult.signingResult?.signatureShares,
9611
+ statechainPublicKeys: response.directFromCpfpRefundTxSigningResult.signingResult?.publicKeys,
9612
+ verifyingKey: response.directFromCpfpRefundTxSigningResult.verifyingKey,
9613
+ statechainCommitments: response.directFromCpfpRefundTxSigningResult.signingResult?.signingNonceCommitments,
9614
+ selfCommitment: newDirectFromCpfpRefundSigningJob.signingNonceCommitment,
9615
+ publicKey: signingPublicKey,
9616
+ selfSignature: directFromCpfpRefundUserSig,
9617
+ adaptorPubKey: new Uint8Array()
9618
+ });
9619
+ }
9620
+ return await sparkClient.finalize_node_signatures_v2({
8669
9621
  intent: 4 /* EXTEND */,
8670
9622
  nodeSignatures: [
8671
9623
  {
8672
9624
  nodeId: response.leafId,
8673
9625
  nodeTxSignature: nodeSig,
8674
- refundTxSignature: refundSig
9626
+ directNodeTxSignature: directNodeSig,
9627
+ refundTxSignature: cpfpRefundSig,
9628
+ directRefundTxSignature: directRefundSig,
9629
+ directFromCpfpRefundTxSignature: directFromCpfpRefundSig
8675
9630
  }
8676
9631
  ]
8677
9632
  });
8678
9633
  }
8679
- async refreshTimelockRefundTx(node) {
9634
+ async testonly_expireTimeLockNodeTx(node, parentNode) {
9635
+ return await this.refreshTimelockNodesInternal(node, parentNode, true);
9636
+ }
9637
+ async testonly_expireTimeLockRefundtx(node) {
8680
9638
  const nodeTx = getTxFromRawTxBytes(node.nodeTx);
8681
- const refundTx = getTxFromRawTxBytes(node.refundTx);
8682
- const currSequence = refundTx.getInput(0).sequence || 0;
8683
- const { nextSequence } = getNextTransactionSequence(currSequence);
8684
- const signingPubKey = await this.config.signer.getPublicKeyFromDerivation({
9639
+ const directNodeTx = node.directTx.length > 0 ? getTxFromRawTxBytes(node.directTx) : void 0;
9640
+ const cpfpRefundTx = getTxFromRawTxBytes(node.refundTx);
9641
+ const currSequence = cpfpRefundTx.getInput(0).sequence || 0;
9642
+ const currTimelock = getCurrentTimelock(currSequence);
9643
+ if (currTimelock <= 100) {
9644
+ throw new ValidationError("Cannot expire timelock below 100", {
9645
+ field: "currTimelock",
9646
+ value: currTimelock,
9647
+ expected: "Timelock greater than 100"
9648
+ });
9649
+ }
9650
+ const nextSequence = TEST_UNILATERAL_SEQUENCE;
9651
+ const nextDirectSequence = TEST_UNILATERAL_SEQUENCE + DIRECT_TIMELOCK_OFFSET;
9652
+ const nodeOutput = nodeTx.getOutput(0);
9653
+ if (!nodeOutput) {
9654
+ throw Error("Could not get node output");
9655
+ }
9656
+ const keyDerivation = {
8685
9657
  type: "leaf" /* LEAF */,
8686
9658
  path: node.id
8687
- });
8688
- const newRefundTx = new Transaction5({
8689
- version: 3,
8690
- allowUnknownOutputs: true
8691
- });
8692
- const originalRefundOutput = refundTx.getOutput(0);
8693
- if (!originalRefundOutput) {
8694
- throw Error("Could not get original refund output");
9659
+ };
9660
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
9661
+ const cpfpRefundOutPoint = {
9662
+ txid: hexToBytes8(getTxId(nodeTx)),
9663
+ index: 0
9664
+ };
9665
+ let directRefundOutPoint;
9666
+ if (directNodeTx) {
9667
+ directRefundOutPoint = {
9668
+ txid: hexToBytes8(getTxId(directNodeTx)),
9669
+ index: 0
9670
+ };
8695
9671
  }
8696
- newRefundTx.addOutput({
8697
- script: originalRefundOutput.script,
8698
- amount: originalRefundOutput.amount
9672
+ const {
9673
+ cpfpRefundTx: newCpfpRefundTx,
9674
+ directRefundTx: newDirectRefundTx,
9675
+ directFromCpfpRefundTx: newDirectFromCpfpRefundTx
9676
+ } = createRefundTxs({
9677
+ sequence: nextSequence,
9678
+ directSequence: nextDirectSequence,
9679
+ input: cpfpRefundOutPoint,
9680
+ directInput: directRefundOutPoint,
9681
+ amountSats: nodeOutput.amount,
9682
+ receivingPubkey: signingPublicKey,
9683
+ network: this.config.getNetwork()
8699
9684
  });
8700
- for (let j = 1; j < refundTx.outputsLength; j++) {
8701
- const additionalOutput = refundTx.getOutput(j);
8702
- if (additionalOutput) {
8703
- newRefundTx.addOutput(additionalOutput);
8704
- }
9685
+ const signingJobs = [];
9686
+ signingJobs.push({
9687
+ signingPublicKey,
9688
+ rawTx: newCpfpRefundTx.toBytes(),
9689
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9690
+ type: "cpfp",
9691
+ parentTxOut: nodeOutput
9692
+ });
9693
+ const directNodeTxOut = directNodeTx?.getOutput(0);
9694
+ if (newDirectRefundTx && directNodeTxOut) {
9695
+ signingJobs.push({
9696
+ signingPublicKey,
9697
+ rawTx: newDirectRefundTx.toBytes(),
9698
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9699
+ type: "direct",
9700
+ parentTxOut: directNodeTxOut
9701
+ });
8705
9702
  }
8706
- const refundTxInput = refundTx.getInput(0);
8707
- if (!refundTxInput) {
8708
- throw Error("refund tx doesn't have input");
9703
+ if (newDirectFromCpfpRefundTx) {
9704
+ signingJobs.push({
9705
+ signingPublicKey,
9706
+ rawTx: newDirectFromCpfpRefundTx.toBytes(),
9707
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
9708
+ type: "directFromCpfp",
9709
+ parentTxOut: nodeOutput
9710
+ });
8709
9711
  }
8710
- newRefundTx.addInput({
8711
- ...refundTxInput,
8712
- sequence: nextSequence
8713
- });
8714
- const refundSigningJob = {
8715
- signingPublicKey: signingPubKey,
8716
- rawTx: newRefundTx.toBytes(),
8717
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
8718
- };
8719
9712
  const sparkClient = await this.connectionManager.createSparkClient(
8720
9713
  this.config.getCoordinatorAddress()
8721
9714
  );
8722
- const response = await sparkClient.refresh_timelock({
9715
+ const response = await sparkClient.refresh_timelock_v2({
8723
9716
  leafId: node.id,
8724
9717
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
8725
- signingJobs: [getSigningJobProto(refundSigningJob)]
9718
+ signingJobs: signingJobs.map(getSigningJobProto)
8726
9719
  });
8727
- if (response.signingResults.length !== 1) {
9720
+ if (response.signingResults.length !== signingJobs.length) {
8728
9721
  throw Error(
8729
- `Expected 1 signing result, got ${response.signingResults.length}`
9722
+ `Expected ${signingJobs.length} signing results, got ${response.signingResults.length}`
8730
9723
  );
8731
9724
  }
8732
- const signingResult = response.signingResults[0];
8733
- if (!signingResult || !refundSigningJob.signingNonceCommitment) {
8734
- throw Error("Signing result or nonce commitment does not exist");
9725
+ let cpfpRefundSignature;
9726
+ let directRefundSignature;
9727
+ let directFromCpfpRefundSignature;
9728
+ for (const [i, signingJob] of signingJobs.entries()) {
9729
+ const signingResult = response.signingResults[i];
9730
+ if (!signingResult) {
9731
+ throw Error("Signing result does not exist");
9732
+ }
9733
+ const rawTx = getTxFromRawTxBytes(signingJob.rawTx);
9734
+ const txOut = signingJob.parentTxOut;
9735
+ const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
9736
+ const userSignature = await this.config.signer.signFrost({
9737
+ message: rawTxSighash,
9738
+ keyDerivation,
9739
+ publicKey: signingPublicKey,
9740
+ verifyingKey: signingResult.verifyingKey,
9741
+ selfCommitment: signingJob.signingNonceCommitment,
9742
+ statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
9743
+ adaptorPubKey: new Uint8Array()
9744
+ });
9745
+ const signature = await this.config.signer.aggregateFrost({
9746
+ message: rawTxSighash,
9747
+ statechainSignatures: signingResult.signingResult?.signatureShares,
9748
+ statechainPublicKeys: signingResult.signingResult?.publicKeys,
9749
+ verifyingKey: signingResult.verifyingKey,
9750
+ statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
9751
+ selfCommitment: signingJob.signingNonceCommitment,
9752
+ publicKey: signingPublicKey,
9753
+ selfSignature: userSignature,
9754
+ adaptorPubKey: new Uint8Array()
9755
+ });
9756
+ if (signingJob.type === "cpfp") {
9757
+ cpfpRefundSignature = signature;
9758
+ } else if (signingJob.type === "direct") {
9759
+ directRefundSignature = signature;
9760
+ } else if (signingJob.type === "directFromCpfp") {
9761
+ directFromCpfpRefundSignature = signature;
9762
+ }
8735
9763
  }
8736
- const rawTx = getTxFromRawTxBytes(refundSigningJob.rawTx);
8737
- const txOut = nodeTx.getOutput(0);
8738
- const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
8739
- const userSignature = await this.config.signer.signFrost({
8740
- message: rawTxSighash,
8741
- keyDerivation: {
8742
- type: "leaf" /* LEAF */,
8743
- path: node.id
8744
- },
8745
- publicKey: signingPubKey,
8746
- verifyingKey: signingResult.verifyingKey,
8747
- selfCommitment: refundSigningJob.signingNonceCommitment,
8748
- statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
8749
- adaptorPubKey: new Uint8Array()
8750
- });
8751
- const signature = await this.config.signer.aggregateFrost({
8752
- message: rawTxSighash,
8753
- statechainSignatures: signingResult.signingResult?.signatureShares,
8754
- statechainPublicKeys: signingResult.signingResult?.publicKeys,
8755
- verifyingKey: signingResult.verifyingKey,
8756
- statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
8757
- selfCommitment: refundSigningJob.signingNonceCommitment,
8758
- publicKey: signingPubKey,
8759
- selfSignature: userSignature,
8760
- adaptorPubKey: new Uint8Array()
8761
- });
8762
- const result = await sparkClient.finalize_node_signatures({
9764
+ const result = await sparkClient.finalize_node_signatures_v2({
8763
9765
  intent: 3 /* REFRESH */,
8764
9766
  nodeSignatures: [
8765
9767
  {
8766
9768
  nodeId: node.id,
8767
9769
  nodeTxSignature: new Uint8Array(),
8768
- refundTxSignature: signature
9770
+ directNodeTxSignature: new Uint8Array(),
9771
+ refundTxSignature: cpfpRefundSignature,
9772
+ directRefundTxSignature: directRefundSignature,
9773
+ directFromCpfpRefundTxSignature: directFromCpfpRefundSignature
8769
9774
  }
8770
9775
  ]
8771
9776
  });
@@ -8784,7 +9789,12 @@ var CoopExitService = class extends BaseTransferService {
8784
9789
  connectorOutputs,
8785
9790
  receiverPubKey
8786
9791
  }) {
8787
- const { transfer, signaturesMap } = await this.signCoopExitRefunds(
9792
+ const {
9793
+ transfer,
9794
+ signaturesMap,
9795
+ directSignaturesMap,
9796
+ directFromCpfpSignaturesMap
9797
+ } = await this.signCoopExitRefunds(
8788
9798
  leaves,
8789
9799
  exitTxId,
8790
9800
  connectorOutputs,
@@ -8793,34 +9803,81 @@ var CoopExitService = class extends BaseTransferService {
8793
9803
  const transferTweak = await this.deliverTransferPackage(
8794
9804
  transfer,
8795
9805
  leaves,
8796
- signaturesMap
9806
+ signaturesMap,
9807
+ directSignaturesMap,
9808
+ directFromCpfpSignaturesMap
8797
9809
  );
8798
- return { transfer: transferTweak, signaturesMap };
9810
+ return {
9811
+ transfer: transferTweak,
9812
+ signaturesMap,
9813
+ directSignaturesMap,
9814
+ directFromCpfpSignaturesMap
9815
+ };
8799
9816
  }
8800
- createConnectorRefundTransaction(sequence, nodeOutPoint, connectorOutput, amountSats, receiverPubKey) {
8801
- const refundTx = new Transaction6();
8802
- if (!nodeOutPoint.txid || nodeOutPoint.index === void 0) {
8803
- throw new ValidationError("Invalid node outpoint", {
8804
- field: "nodeOutPoint",
8805
- value: { txid: nodeOutPoint.txid, index: nodeOutPoint.index },
9817
+ createConnectorRefundTransactions(sequence, directSequence, cpfpNodeOutPoint, directNodeOutPoint, connectorOutput, amountSats, receiverPubKey) {
9818
+ const cpfpRefundTx = new Transaction6();
9819
+ if (!cpfpNodeOutPoint.txid || cpfpNodeOutPoint.index === void 0) {
9820
+ throw new ValidationError("Invalid CPFP node outpoint", {
9821
+ field: "cpfpNodeOutPoint",
9822
+ value: { txid: cpfpNodeOutPoint.txid, index: cpfpNodeOutPoint.index },
8806
9823
  expected: "Both txid and index must be defined"
8807
9824
  });
8808
9825
  }
8809
- refundTx.addInput({
8810
- txid: nodeOutPoint.txid,
8811
- index: nodeOutPoint.index,
9826
+ cpfpRefundTx.addInput({
9827
+ txid: cpfpNodeOutPoint.txid,
9828
+ index: cpfpNodeOutPoint.index,
8812
9829
  sequence
8813
9830
  });
8814
- refundTx.addInput(connectorOutput);
9831
+ cpfpRefundTx.addInput(connectorOutput);
8815
9832
  const receiverScript = getP2TRScriptFromPublicKey(
8816
9833
  receiverPubKey,
8817
9834
  this.config.getNetwork()
8818
9835
  );
8819
- refundTx.addOutput({
9836
+ cpfpRefundTx.addOutput({
8820
9837
  script: receiverScript,
8821
9838
  amount: amountSats
8822
9839
  });
8823
- return refundTx;
9840
+ let directRefundTx;
9841
+ let directFromCpfpRefundTx;
9842
+ if (directNodeOutPoint) {
9843
+ if (!directNodeOutPoint.txid || directNodeOutPoint.index === void 0) {
9844
+ throw new ValidationError("Invalid direct node outpoint", {
9845
+ field: "directNodeOutPoint",
9846
+ value: {
9847
+ txid: directNodeOutPoint.txid,
9848
+ index: directNodeOutPoint.index
9849
+ },
9850
+ expected: "Both txid and index must be defined"
9851
+ });
9852
+ }
9853
+ directRefundTx = new Transaction6();
9854
+ directRefundTx.addInput({
9855
+ txid: directNodeOutPoint.txid,
9856
+ index: directNodeOutPoint.index,
9857
+ sequence: directSequence
9858
+ });
9859
+ directRefundTx.addInput(connectorOutput);
9860
+ directRefundTx.addOutput({
9861
+ script: receiverScript,
9862
+ amount: maybeApplyFee(amountSats)
9863
+ });
9864
+ directFromCpfpRefundTx = new Transaction6();
9865
+ directFromCpfpRefundTx.addInput({
9866
+ txid: cpfpNodeOutPoint.txid,
9867
+ index: cpfpNodeOutPoint.index,
9868
+ sequence: directSequence
9869
+ });
9870
+ directFromCpfpRefundTx.addInput(connectorOutput);
9871
+ directFromCpfpRefundTx.addOutput({
9872
+ script: receiverScript,
9873
+ amount: maybeApplyFee(amountSats)
9874
+ });
9875
+ }
9876
+ return {
9877
+ cpfpRefundTx,
9878
+ directRefundTx,
9879
+ directFromCpfpRefundTx
9880
+ };
8824
9881
  }
8825
9882
  async signCoopExitRefunds(leaves, exitTxId, connectorOutputs, receiverPubKey) {
8826
9883
  if (leaves.length !== connectorOutputs.length) {
@@ -8856,39 +9913,67 @@ var CoopExitService = class extends BaseTransferService {
8856
9913
  });
8857
9914
  }
8858
9915
  const currentRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
8859
- const { nextSequence } = getNextTransactionSequence(
8860
- currentRefundTx.getInput(0).sequence
8861
- );
8862
- const refundTx = this.createConnectorRefundTransaction(
9916
+ const sequence = currentRefundTx.getInput(0).sequence;
9917
+ if (!sequence) {
9918
+ throw new ValidationError("Invalid refund transaction", {
9919
+ field: "sequence",
9920
+ value: currentRefundTx.getInput(0),
9921
+ expected: "Non-null sequence"
9922
+ });
9923
+ }
9924
+ const { nextSequence, nextDirectSequence } = getNextTransactionSequence(sequence);
9925
+ let currentDirectRefundTx;
9926
+ if (leaf.leaf.directRefundTx.length > 0) {
9927
+ currentDirectRefundTx = getTxFromRawTxBytes(leaf.leaf.directRefundTx);
9928
+ }
9929
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = this.createConnectorRefundTransactions(
8863
9930
  nextSequence,
9931
+ nextDirectSequence,
8864
9932
  currentRefundTx.getInput(0),
9933
+ currentDirectRefundTx?.getInput(0),
8865
9934
  connectorOutput,
8866
9935
  BigInt(leaf.leaf.value),
8867
9936
  receiverPubKey
8868
9937
  );
8869
9938
  const signingNonceCommitment = await this.config.signer.getRandomSigningCommitment();
9939
+ const directSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
9940
+ const directFromCpfpSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
9941
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(leaf.keyDerivation);
8870
9942
  const signingJob = {
8871
9943
  leafId: leaf.leaf.id,
8872
9944
  refundTxSigningJob: {
8873
9945
  signingPublicKey: await this.config.signer.getPublicKeyFromDerivation(
8874
9946
  leaf.keyDerivation
8875
9947
  ),
8876
- rawTx: refundTx.toBytes(),
9948
+ rawTx: cpfpRefundTx.toBytes(),
8877
9949
  signingNonceCommitment: signingNonceCommitment.commitment
8878
9950
  },
8879
- // TODO: Add direct refund signature
8880
- directRefundTxSigningJob: void 0,
8881
- directFromCpfpRefundTxSigningJob: void 0
9951
+ directRefundTxSigningJob: directRefundTx ? {
9952
+ signingPublicKey,
9953
+ rawTx: directRefundTx.toBytes(),
9954
+ signingNonceCommitment: directSigningNonceCommitment.commitment
9955
+ } : void 0,
9956
+ directFromCpfpRefundTxSigningJob: directFromCpfpRefundTx ? {
9957
+ signingPublicKey,
9958
+ rawTx: directFromCpfpRefundTx.toBytes(),
9959
+ signingNonceCommitment: directFromCpfpSigningNonceCommitment.commitment
9960
+ } : void 0
8882
9961
  };
8883
9962
  signingJobs.push(signingJob);
8884
9963
  const tx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
9964
+ const directTx = leaf.leaf.directTx.length > 0 ? getTxFromRawTxBytes(leaf.leaf.directTx) : void 0;
8885
9965
  leafDataMap.set(leaf.leaf.id, {
8886
9966
  keyDerivation: leaf.keyDerivation,
8887
- refundTx,
9967
+ receivingPubkey: receiverPubKey,
8888
9968
  signingNonceCommitment,
9969
+ directSigningNonceCommitment,
8889
9970
  tx,
8890
- vout: leaf.leaf.vout,
8891
- receivingPubkey: receiverPubKey
9971
+ directTx,
9972
+ refundTx: cpfpRefundTx,
9973
+ directRefundTx,
9974
+ directFromCpfpRefundTx,
9975
+ directFromCpfpRefundSigningNonceCommitment: directFromCpfpSigningNonceCommitment,
9976
+ vout: leaf.leaf.vout
8892
9977
  });
8893
9978
  }
8894
9979
  const sparkClient = await this.connectionManager.createSparkClient(
@@ -8896,7 +9981,7 @@ var CoopExitService = class extends BaseTransferService {
8896
9981
  );
8897
9982
  let response;
8898
9983
  try {
8899
- response = await sparkClient.cooperative_exit({
9984
+ response = await sparkClient.cooperative_exit_v2({
8900
9985
  transfer: {
8901
9986
  transferId: uuidv73(),
8902
9987
  leavesToSend: signingJobs,
@@ -8930,10 +10015,25 @@ var CoopExitService = class extends BaseTransferService {
8930
10015
  response.signingResults
8931
10016
  );
8932
10017
  const signaturesMap = /* @__PURE__ */ new Map();
10018
+ const directSignaturesMap = /* @__PURE__ */ new Map();
10019
+ const directFromCpfpSignaturesMap = /* @__PURE__ */ new Map();
8933
10020
  for (const signature of signatures) {
8934
10021
  signaturesMap.set(signature.nodeId, signature.refundTxSignature);
10022
+ directSignaturesMap.set(
10023
+ signature.nodeId,
10024
+ signature.directRefundTxSignature
10025
+ );
10026
+ directFromCpfpSignaturesMap.set(
10027
+ signature.nodeId,
10028
+ signature.directFromCpfpRefundTxSignature
10029
+ );
8935
10030
  }
8936
- return { transfer: response.transfer, signaturesMap };
10031
+ return {
10032
+ transfer: response.transfer,
10033
+ signaturesMap,
10034
+ directSignaturesMap,
10035
+ directFromCpfpSignaturesMap
10036
+ };
8937
10037
  }
8938
10038
  };
8939
10039
 
@@ -9130,7 +10230,8 @@ var LightningService = class {
9130
10230
  let signingCommitments;
9131
10231
  try {
9132
10232
  signingCommitments = await sparkClient.get_signing_commitments({
9133
- nodeIds: leaves.map((leaf) => leaf.leaf.id)
10233
+ nodeIds: leaves.map((leaf) => leaf.leaf.id),
10234
+ count: 3
9134
10235
  });
9135
10236
  } catch (error) {
9136
10237
  throw new NetworkError(
@@ -9143,10 +10244,19 @@ var LightningService = class {
9143
10244
  error
9144
10245
  );
9145
10246
  }
9146
- const leafSigningJobs = await this.signingService.signRefunds(
10247
+ const {
10248
+ cpfpLeafSigningJobs,
10249
+ directLeafSigningJobs,
10250
+ directFromCpfpLeafSigningJobs
10251
+ } = await this.signingService.signRefunds(
9147
10252
  leaves,
9148
- signingCommitments.signingCommitments,
9149
- receiverIdentityPubkey
10253
+ receiverIdentityPubkey,
10254
+ signingCommitments.signingCommitments.slice(0, leaves.length),
10255
+ signingCommitments.signingCommitments.slice(
10256
+ leaves.length,
10257
+ 2 * leaves.length
10258
+ ),
10259
+ signingCommitments.signingCommitments.slice(2 * leaves.length)
9150
10260
  );
9151
10261
  const transferId = uuidv74();
9152
10262
  let bolt11String = "";
@@ -9183,7 +10293,7 @@ var LightningService = class {
9183
10293
  const reason = isInboundPayment ? 1 /* REASON_RECEIVE */ : 0 /* REASON_SEND */;
9184
10294
  let response;
9185
10295
  try {
9186
- response = await sparkClient.initiate_preimage_swap({
10296
+ response = await sparkClient.initiate_preimage_swap_v2({
9187
10297
  paymentHash,
9188
10298
  invoiceAmount: {
9189
10299
  invoiceAmountProof: {
@@ -9195,7 +10305,9 @@ var LightningService = class {
9195
10305
  transfer: {
9196
10306
  transferId,
9197
10307
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
9198
- leavesToSend: leafSigningJobs,
10308
+ leavesToSend: cpfpLeafSigningJobs,
10309
+ directLeavesToSend: directLeafSigningJobs,
10310
+ directFromCpfpLeavesToSend: directFromCpfpLeafSigningJobs,
9199
10311
  receiverIdentityPublicKey: receiverIdentityPubkey,
9200
10312
  expiryTime: new Date(Date.now() + 2 * 60 * 1e3)
9201
10313
  },
@@ -9278,14 +10390,7 @@ var LightningService = class {
9278
10390
 
9279
10391
  // src/services/tree-creation.ts
9280
10392
  import { hexToBytes as hexToBytes10 } from "@noble/curves/abstract/utils";
9281
- import { Address as Address4, OutScript as OutScript4, Transaction as Transaction7 } from "@scure/btc-signer";
9282
- var INITIAL_TIME_LOCK3 = 2e3;
9283
- function maybeApplyFee2(amount) {
9284
- if (amount > BigInt(DEFAULT_FEE_SATS)) {
9285
- return amount - BigInt(DEFAULT_FEE_SATS);
9286
- }
9287
- return amount;
9288
- }
10393
+ import { Address as Address3, OutScript as OutScript3 } from "@scure/btc-signer";
9289
10394
  var TreeCreationService = class {
9290
10395
  config;
9291
10396
  connectionManager;
@@ -9400,7 +10505,7 @@ var TreeCreationService = class {
9400
10505
  );
9401
10506
  let response;
9402
10507
  try {
9403
- response = await sparkClient.create_tree(request);
10508
+ response = await sparkClient.create_tree_v2(request);
9404
10509
  } catch (error) {
9405
10510
  throw new Error(`Error creating tree: ${error}`);
9406
10511
  }
@@ -9417,7 +10522,7 @@ var TreeCreationService = class {
9417
10522
  );
9418
10523
  let finalizeResp;
9419
10524
  try {
9420
- finalizeResp = await sparkClient.finalize_node_signatures({
10525
+ finalizeResp = await sparkClient.finalize_node_signatures_v2({
9421
10526
  nodeSignatures
9422
10527
  });
9423
10528
  } catch (error) {
@@ -9482,91 +10587,111 @@ var TreeCreationService = class {
9482
10587
  async buildChildCreationNode(node, parentTx, vout, network) {
9483
10588
  const internalCreationNode = {
9484
10589
  nodeTxSigningJob: void 0,
9485
- refundTxSigningJob: void 0,
9486
- children: [],
9487
10590
  directNodeTxSigningJob: void 0,
10591
+ refundTxSigningJob: void 0,
9488
10592
  directRefundTxSigningJob: void 0,
9489
- directFromCpfpRefundTxSigningJob: void 0
10593
+ directFromCpfpRefundTxSigningJob: void 0,
10594
+ children: []
9490
10595
  };
9491
- const tx = new Transaction7({ version: 3 });
9492
- tx.addInput({
9493
- txid: getTxId(parentTx),
9494
- index: vout
9495
- });
9496
10596
  const parentTxOut = parentTx.getOutput(vout);
9497
10597
  if (!parentTxOut?.script || !parentTxOut?.amount) {
9498
10598
  throw new Error("parentTxOut is undefined");
9499
10599
  }
9500
- tx.addOutput({
10600
+ const parentOutPoint = {
10601
+ txid: hexToBytes10(getTxId(parentTx)),
10602
+ index: vout
10603
+ };
10604
+ const parentTxOutObj = {
9501
10605
  script: parentTxOut.script,
9502
10606
  amount: parentTxOut.amount
9503
- // maybeApplyFee(parentTxOut.amount),
9504
- });
9505
- tx.addOutput(getEphemeralAnchorOutput());
9506
- const signingNonceCommitment = await this.config.signer.getRandomSigningCommitment();
9507
- const signingJob = {
10607
+ };
10608
+ const { cpfpNodeTx, directNodeTx } = createNodeTxs(
10609
+ parentTxOutObj,
10610
+ parentOutPoint
10611
+ );
10612
+ const cpfpNodeSigningCommitment = await this.config.signer.getRandomSigningCommitment();
10613
+ const directNodeSigningCommitment = await this.config.signer.getRandomSigningCommitment();
10614
+ const cpfpNodeSigningJob = {
9508
10615
  signingPublicKey: node.signingPublicKey,
9509
- rawTx: tx.toBytes(),
9510
- signingNonceCommitment: signingNonceCommitment.commitment
10616
+ rawTx: cpfpNodeTx.toBytes(),
10617
+ signingNonceCommitment: cpfpNodeSigningCommitment.commitment
9511
10618
  };
9512
- internalCreationNode.nodeTxSigningCommitment = signingNonceCommitment;
9513
- internalCreationNode.nodeTxSigningJob = signingJob;
9514
- const sequence = 1 << 30 | INITIAL_TIME_LOCK3;
10619
+ const directNodeSigningJob = directNodeTx ? {
10620
+ signingPublicKey: node.signingPublicKey,
10621
+ rawTx: directNodeTx.toBytes(),
10622
+ signingNonceCommitment: directNodeSigningCommitment.commitment
10623
+ } : void 0;
10624
+ internalCreationNode.nodeTxSigningCommitment = cpfpNodeSigningCommitment;
10625
+ internalCreationNode.directNodeTxSigningCommitment = directNodeSigningCommitment;
10626
+ internalCreationNode.nodeTxSigningJob = cpfpNodeSigningJob;
10627
+ internalCreationNode.directNodeTxSigningJob = directNodeSigningJob;
10628
+ const sequence = INITIAL_SEQUENCE;
10629
+ const directSequence = INITIAL_DIRECT_SEQUENCE;
9515
10630
  const childCreationNode = {
9516
10631
  nodeTxSigningJob: void 0,
9517
- refundTxSigningJob: void 0,
9518
- children: [],
9519
10632
  directNodeTxSigningJob: void 0,
10633
+ refundTxSigningJob: void 0,
9520
10634
  directRefundTxSigningJob: void 0,
9521
- directFromCpfpRefundTxSigningJob: void 0
10635
+ directFromCpfpRefundTxSigningJob: void 0,
10636
+ children: []
9522
10637
  };
9523
- const childTx = new Transaction7({ version: 3 });
9524
- childTx.addInput({
9525
- txid: getTxId(tx),
9526
- index: 0,
9527
- sequence
9528
- });
9529
- childTx.addOutput({
9530
- script: parentTxOut.script,
9531
- amount: parentTxOut.amount
9532
- // maybeApplyFee(parentTxOut.amount),
9533
- });
9534
- childTx.addOutput(getEphemeralAnchorOutput());
9535
- const childSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
9536
- const childSigningJob = {
10638
+ const [cpfpLeafTx, directLeafTx] = createLeafNodeTx(
10639
+ sequence,
10640
+ directSequence,
10641
+ { txid: hexToBytes10(getTxId(cpfpNodeTx)), index: 0 },
10642
+ parentTxOutObj,
10643
+ true
10644
+ // shouldCalculateFee
10645
+ );
10646
+ const cpfpLeafSigningCommitment = await this.config.signer.getRandomSigningCommitment();
10647
+ const directLeafSigningCommitment = await this.config.signer.getRandomSigningCommitment();
10648
+ const cpfpLeafSigningJob = {
9537
10649
  signingPublicKey: node.signingPublicKey,
9538
- rawTx: childTx.toBytes(),
9539
- signingNonceCommitment: childSigningNonceCommitment.commitment
10650
+ rawTx: cpfpLeafTx.toBytes(),
10651
+ signingNonceCommitment: cpfpLeafSigningCommitment.commitment
9540
10652
  };
9541
- childCreationNode.nodeTxSigningCommitment = childSigningNonceCommitment;
9542
- childCreationNode.nodeTxSigningJob = childSigningJob;
9543
- const refundTx = new Transaction7({ version: 3 });
9544
- refundTx.addInput({
9545
- txid: getTxId(childTx),
9546
- index: 0,
9547
- sequence
9548
- });
9549
- const refundP2trAddress = getP2TRAddressFromPublicKey(
9550
- node.signingPublicKey,
10653
+ const directLeafSigningJob = {
10654
+ signingPublicKey: node.signingPublicKey,
10655
+ rawTx: directLeafTx.toBytes(),
10656
+ signingNonceCommitment: directLeafSigningCommitment.commitment
10657
+ };
10658
+ childCreationNode.nodeTxSigningCommitment = cpfpLeafSigningCommitment;
10659
+ childCreationNode.directNodeTxSigningCommitment = directLeafSigningCommitment;
10660
+ childCreationNode.nodeTxSigningJob = cpfpLeafSigningJob;
10661
+ childCreationNode.directNodeTxSigningJob = directLeafSigningJob;
10662
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
10663
+ sequence,
10664
+ directSequence,
10665
+ input: { txid: hexToBytes10(getTxId(cpfpLeafTx)), index: 0 },
10666
+ directInput: { txid: hexToBytes10(getTxId(directLeafTx)), index: 0 },
10667
+ amountSats: parentTxOut.amount,
10668
+ receivingPubkey: node.signingPublicKey,
9551
10669
  network
9552
- );
9553
- const refundAddress = Address4(getNetwork(network)).decode(
9554
- refundP2trAddress
9555
- );
9556
- const refundPkScript = OutScript4.encode(refundAddress);
9557
- refundTx.addOutput({
9558
- script: refundPkScript,
9559
- amount: maybeApplyFee2(parentTxOut.amount)
9560
10670
  });
9561
- refundTx.addOutput(getEphemeralAnchorOutput());
9562
- const refundSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
9563
- const refundSigningJob = {
10671
+ const cpfpRefundSigningCommitment = await this.config.signer.getRandomSigningCommitment();
10672
+ const directRefundSigningCommitment = await this.config.signer.getRandomSigningCommitment();
10673
+ const directFromCpfpRefundSigningCommitment = await this.config.signer.getRandomSigningCommitment();
10674
+ const cpfpRefundSigningJob = {
9564
10675
  signingPublicKey: node.signingPublicKey,
9565
- rawTx: refundTx.toBytes(),
9566
- signingNonceCommitment: refundSigningNonceCommitment.commitment
10676
+ rawTx: cpfpRefundTx.toBytes(),
10677
+ signingNonceCommitment: cpfpRefundSigningCommitment.commitment
9567
10678
  };
9568
- childCreationNode.refundTxSigningCommitment = refundSigningNonceCommitment;
9569
- childCreationNode.refundTxSigningJob = refundSigningJob;
10679
+ const directRefundSigningJob = directRefundTx ? {
10680
+ signingPublicKey: node.signingPublicKey,
10681
+ rawTx: directRefundTx.toBytes(),
10682
+ signingNonceCommitment: directRefundSigningCommitment.commitment
10683
+ } : void 0;
10684
+ const directFromCpfpRefundSigningJob = directFromCpfpRefundTx ? {
10685
+ signingPublicKey: node.signingPublicKey,
10686
+ rawTx: directFromCpfpRefundTx.toBytes(),
10687
+ signingNonceCommitment: directFromCpfpRefundSigningCommitment.commitment
10688
+ } : void 0;
10689
+ childCreationNode.refundTxSigningCommitment = cpfpRefundSigningCommitment;
10690
+ childCreationNode.directRefundTxSigningCommitment = directRefundSigningCommitment;
10691
+ childCreationNode.directFromCpfpRefundTxSigningCommitment = directFromCpfpRefundSigningCommitment;
10692
+ childCreationNode.refundTxSigningJob = cpfpRefundSigningJob;
10693
+ childCreationNode.directRefundTxSigningJob = directRefundSigningJob;
10694
+ childCreationNode.directFromCpfpRefundTxSigningJob = directFromCpfpRefundSigningJob;
9570
10695
  internalCreationNode.children.push(childCreationNode);
9571
10696
  return internalCreationNode;
9572
10697
  }
@@ -9575,40 +10700,49 @@ var TreeCreationService = class {
9575
10700
  if (!parentTxOutput?.script || !parentTxOutput?.amount) {
9576
10701
  throw new Error("parentTxOutput is undefined");
9577
10702
  }
9578
- const rootNodeTx = new Transaction7({ version: 3 });
9579
- rootNodeTx.addInput({
9580
- txid: getTxId(parentTx),
9581
- index: vout
9582
- });
10703
+ const childTxOuts = [];
9583
10704
  for (let i = 0; i < root.children.length; i++) {
9584
10705
  const child = root.children[i];
9585
10706
  if (!child || !child.address) {
9586
10707
  throw new Error("child address is undefined");
9587
10708
  }
9588
- const childAddress = Address4(getNetwork(network)).decode(child.address);
9589
- const childPkScript = OutScript4.encode(childAddress);
9590
- rootNodeTx.addOutput({
10709
+ const childAddress = Address3(getNetwork(network)).decode(child.address);
10710
+ const childPkScript = OutScript3.encode(childAddress);
10711
+ childTxOuts.push({
9591
10712
  script: childPkScript,
9592
10713
  amount: parentTxOutput.amount / 2n
9593
- // feeAdjustedAmount / 2n,
9594
10714
  });
9595
10715
  }
9596
- rootNodeTx.addOutput(getEphemeralAnchorOutput());
9597
- const rootNodeSigningCommitment = await this.config.signer.getRandomSigningCommitment();
9598
- const rootNodeSigningJob = {
10716
+ const parentOutPoint = {
10717
+ txid: hexToBytes10(getTxId(parentTx)),
10718
+ index: vout
10719
+ };
10720
+ const [cpfpSplitTx, directSplitTx] = createSplitTx(
10721
+ parentOutPoint,
10722
+ childTxOuts
10723
+ );
10724
+ const cpfpSplitSigningCommitment = await this.config.signer.getRandomSigningCommitment();
10725
+ const directSplitSigningCommitment = await this.config.signer.getRandomSigningCommitment();
10726
+ const cpfpSplitSigningJob = {
10727
+ signingPublicKey: root.signingPublicKey,
10728
+ rawTx: cpfpSplitTx.toBytes(),
10729
+ signingNonceCommitment: cpfpSplitSigningCommitment.commitment
10730
+ };
10731
+ const directSplitSigningJob = {
9599
10732
  signingPublicKey: root.signingPublicKey,
9600
- rawTx: rootNodeTx.toBytes(),
9601
- signingNonceCommitment: rootNodeSigningCommitment.commitment
10733
+ rawTx: directSplitTx.toBytes(),
10734
+ signingNonceCommitment: directSplitSigningCommitment.commitment
9602
10735
  };
9603
10736
  const rootCreationNode = {
9604
- nodeTxSigningJob: rootNodeSigningJob,
10737
+ nodeTxSigningJob: cpfpSplitSigningJob,
10738
+ directNodeTxSigningJob: directSplitSigningJob,
9605
10739
  refundTxSigningJob: void 0,
9606
- children: [],
9607
- directNodeTxSigningJob: void 0,
9608
10740
  directRefundTxSigningJob: void 0,
9609
- directFromCpfpRefundTxSigningJob: void 0
10741
+ directFromCpfpRefundTxSigningJob: void 0,
10742
+ children: []
9610
10743
  };
9611
- rootCreationNode.nodeTxSigningCommitment = rootNodeSigningCommitment;
10744
+ rootCreationNode.nodeTxSigningCommitment = cpfpSplitSigningCommitment;
10745
+ rootCreationNode.directNodeTxSigningCommitment = directSplitSigningCommitment;
9612
10746
  const leftChild = root.children[0];
9613
10747
  const rightChild = root.children[1];
9614
10748
  if (!leftChild || !rightChild) {
@@ -9616,13 +10750,15 @@ var TreeCreationService = class {
9616
10750
  }
9617
10751
  const leftChildCreationNode = await this.buildChildCreationNode(
9618
10752
  leftChild,
9619
- rootNodeTx,
10753
+ cpfpSplitTx,
10754
+ // Use CPFP version for children
9620
10755
  0,
9621
10756
  network
9622
10757
  );
9623
10758
  const rightChildCreationNode = await this.buildChildCreationNode(
9624
10759
  rightChild,
9625
- rootNodeTx,
10760
+ cpfpSplitTx,
10761
+ // Use CPFP version for children
9626
10762
  1,
9627
10763
  network
9628
10764
  );
@@ -9631,19 +10767,19 @@ var TreeCreationService = class {
9631
10767
  return rootCreationNode;
9632
10768
  }
9633
10769
  async signNodeCreation(parentTx, vout, internalNode, creationNode, creationResponseNode) {
9634
- if (!creationNode.nodeTxSigningJob?.signingPublicKey || !internalNode.verificationKey) {
10770
+ if (!creationNode.nodeTxSigningJob?.signingPublicKey || !creationNode.directNodeTxSigningJob?.signingPublicKey || !internalNode.verificationKey) {
9635
10771
  throw new Error("signingPublicKey or verificationKey is undefined");
9636
10772
  }
9637
10773
  const parentTxOutput = parentTx.getOutput(vout);
9638
10774
  if (!parentTxOutput) {
9639
10775
  throw new Error("parentTxOutput is undefined");
9640
10776
  }
9641
- const tx = getTxFromRawTxBytes(creationNode.nodeTxSigningJob.rawTx);
9642
- const txSighash = getSigHashFromTx(tx, 0, parentTxOutput);
9643
- let nodeTxSignature = new Uint8Array();
10777
+ const cpfpNodeTx = getTxFromRawTxBytes(creationNode.nodeTxSigningJob.rawTx);
10778
+ const cpfpNodeTxSighash = getSigHashFromTx(cpfpNodeTx, 0, parentTxOutput);
10779
+ let cpfpNodeTxSignature = new Uint8Array();
9644
10780
  if (creationNode.nodeTxSigningCommitment) {
9645
- const userSignature = await this.config.signer.signFrost({
9646
- message: txSighash,
10781
+ const cpfpUserSignature = await this.config.signer.signFrost({
10782
+ message: cpfpNodeTxSighash,
9647
10783
  publicKey: creationNode.nodeTxSigningJob.signingPublicKey,
9648
10784
  keyDerivation: {
9649
10785
  type: "leaf" /* LEAF */,
@@ -9653,30 +10789,84 @@ var TreeCreationService = class {
9653
10789
  statechainCommitments: creationResponseNode.nodeTxSigningResult?.signingNonceCommitments,
9654
10790
  verifyingKey: internalNode.verificationKey
9655
10791
  });
9656
- nodeTxSignature = await this.config.signer.aggregateFrost({
9657
- message: txSighash,
10792
+ cpfpNodeTxSignature = await this.config.signer.aggregateFrost({
10793
+ message: cpfpNodeTxSighash,
9658
10794
  statechainSignatures: creationResponseNode.nodeTxSigningResult?.signatureShares,
9659
10795
  statechainPublicKeys: creationResponseNode.nodeTxSigningResult?.publicKeys,
9660
10796
  verifyingKey: internalNode.verificationKey,
9661
10797
  statechainCommitments: creationResponseNode.nodeTxSigningResult?.signingNonceCommitments,
9662
10798
  selfCommitment: creationNode.nodeTxSigningCommitment,
9663
- selfSignature: userSignature,
10799
+ selfSignature: cpfpUserSignature,
10800
+ publicKey: internalNode.signingPublicKey
10801
+ });
10802
+ }
10803
+ const directNodeTx = getTxFromRawTxBytes(
10804
+ creationNode.directNodeTxSigningJob.rawTx
10805
+ );
10806
+ const directNodeTxSighash = getSigHashFromTx(
10807
+ directNodeTx,
10808
+ 0,
10809
+ parentTxOutput
10810
+ );
10811
+ let directNodeTxSignature = new Uint8Array();
10812
+ if (creationNode.directNodeTxSigningCommitment) {
10813
+ const directUserSignature = await this.config.signer.signFrost({
10814
+ message: directNodeTxSighash,
10815
+ publicKey: creationNode.directNodeTxSigningJob.signingPublicKey,
10816
+ keyDerivation: {
10817
+ type: "leaf" /* LEAF */,
10818
+ path: creationResponseNode.nodeId
10819
+ },
10820
+ selfCommitment: creationNode.directNodeTxSigningCommitment,
10821
+ statechainCommitments: creationResponseNode.directNodeTxSigningResult?.signingNonceCommitments,
10822
+ verifyingKey: internalNode.verificationKey
10823
+ });
10824
+ directNodeTxSignature = await this.config.signer.aggregateFrost({
10825
+ message: directNodeTxSighash,
10826
+ statechainSignatures: creationResponseNode.directNodeTxSigningResult?.signatureShares,
10827
+ statechainPublicKeys: creationResponseNode.directNodeTxSigningResult?.publicKeys,
10828
+ verifyingKey: internalNode.verificationKey,
10829
+ statechainCommitments: creationResponseNode.directNodeTxSigningResult?.signingNonceCommitments,
10830
+ selfCommitment: creationNode.directNodeTxSigningCommitment,
10831
+ selfSignature: directUserSignature,
9664
10832
  publicKey: internalNode.signingPublicKey
9665
10833
  });
9666
10834
  }
9667
- let refundTxSignature = new Uint8Array();
9668
- if (creationNode.refundTxSigningCommitment) {
9669
- const rawTx = creationNode.refundTxSigningJob?.rawTx;
9670
- if (!rawTx) {
9671
- throw new Error("rawTx is undefined");
10835
+ let cpfpRefundTxSignature = new Uint8Array();
10836
+ let directRefundTxSignature = new Uint8Array();
10837
+ let directFromCpfpRefundTxSignature = new Uint8Array();
10838
+ if (creationNode.refundTxSigningCommitment && creationNode.directRefundTxSigningCommitment && creationNode.directFromCpfpRefundTxSigningCommitment) {
10839
+ const rawCpfpRefundTx = creationNode.refundTxSigningJob?.rawTx;
10840
+ const rawDirectRefundTx = creationNode.directRefundTxSigningJob?.rawTx;
10841
+ const rawDirectFromCpfpRefundTx = creationNode.directFromCpfpRefundTxSigningJob?.rawTx;
10842
+ if (!rawCpfpRefundTx || !rawDirectRefundTx || !rawDirectFromCpfpRefundTx) {
10843
+ throw new Error("refund transaction rawTx is undefined");
9672
10844
  }
9673
- if (!creationNode.refundTxSigningJob?.signingPublicKey) {
9674
- throw new Error("signingPublicKey is undefined");
10845
+ if (!creationNode.refundTxSigningJob?.signingPublicKey || !creationNode.directRefundTxSigningJob?.signingPublicKey || !creationNode.directFromCpfpRefundTxSigningJob?.signingPublicKey) {
10846
+ throw new Error("refund transaction signingPublicKey is undefined");
9675
10847
  }
9676
- const refundTx = getTxFromRawTxBytes(rawTx);
9677
- const refundTxSighash = getSigHashFromTx(refundTx, 0, parentTxOutput);
9678
- const refundSigningResponse = await this.config.signer.signFrost({
9679
- message: refundTxSighash,
10848
+ const cpfpRefundTx = getTxFromRawTxBytes(rawCpfpRefundTx);
10849
+ const directRefundTx = getTxFromRawTxBytes(rawDirectRefundTx);
10850
+ const directFromCpfpRefundTx = getTxFromRawTxBytes(
10851
+ rawDirectFromCpfpRefundTx
10852
+ );
10853
+ const cpfpRefundTxSighash = getSigHashFromTx(
10854
+ cpfpRefundTx,
10855
+ 0,
10856
+ cpfpNodeTx.getOutput(0)
10857
+ );
10858
+ const directRefundTxSighash = getSigHashFromTx(
10859
+ directRefundTx,
10860
+ 0,
10861
+ directNodeTx.getOutput(0)
10862
+ );
10863
+ const directFromCpfpRefundTxSighash = getSigHashFromTx(
10864
+ directFromCpfpRefundTx,
10865
+ 0,
10866
+ cpfpNodeTx.getOutput(0)
10867
+ );
10868
+ const cpfpRefundUserSignature = await this.config.signer.signFrost({
10869
+ message: cpfpRefundTxSighash,
9680
10870
  publicKey: creationNode.refundTxSigningJob.signingPublicKey,
9681
10871
  keyDerivation: {
9682
10872
  type: "leaf" /* LEAF */,
@@ -9686,27 +10876,69 @@ var TreeCreationService = class {
9686
10876
  statechainCommitments: creationResponseNode.refundTxSigningResult?.signingNonceCommitments,
9687
10877
  verifyingKey: internalNode.verificationKey
9688
10878
  });
9689
- refundTxSignature = await this.config.signer.aggregateFrost({
9690
- message: refundTxSighash,
10879
+ cpfpRefundTxSignature = await this.config.signer.aggregateFrost({
10880
+ message: cpfpRefundTxSighash,
9691
10881
  statechainSignatures: creationResponseNode.refundTxSigningResult?.signatureShares,
9692
10882
  statechainPublicKeys: creationResponseNode.refundTxSigningResult?.publicKeys,
9693
10883
  verifyingKey: internalNode.verificationKey,
9694
10884
  statechainCommitments: creationResponseNode.refundTxSigningResult?.signingNonceCommitments,
9695
10885
  selfCommitment: creationNode.refundTxSigningCommitment,
9696
- selfSignature: refundSigningResponse,
10886
+ selfSignature: cpfpRefundUserSignature,
10887
+ publicKey: internalNode.signingPublicKey
10888
+ });
10889
+ const keyDerivation = {
10890
+ type: "leaf" /* LEAF */,
10891
+ path: creationResponseNode.nodeId
10892
+ };
10893
+ const directRefundUserSignature = await this.config.signer.signFrost({
10894
+ message: directRefundTxSighash,
10895
+ publicKey: creationNode.directRefundTxSigningJob.signingPublicKey,
10896
+ keyDerivation,
10897
+ selfCommitment: creationNode.directRefundTxSigningCommitment,
10898
+ statechainCommitments: creationResponseNode.directRefundTxSigningResult?.signingNonceCommitments,
10899
+ verifyingKey: internalNode.verificationKey
10900
+ });
10901
+ directRefundTxSignature = await this.config.signer.aggregateFrost({
10902
+ message: directRefundTxSighash,
10903
+ statechainSignatures: creationResponseNode.directRefundTxSigningResult?.signatureShares,
10904
+ statechainPublicKeys: creationResponseNode.directRefundTxSigningResult?.publicKeys,
10905
+ verifyingKey: internalNode.verificationKey,
10906
+ statechainCommitments: creationResponseNode.directRefundTxSigningResult?.signingNonceCommitments,
10907
+ selfCommitment: creationNode.directRefundTxSigningCommitment,
10908
+ selfSignature: directRefundUserSignature,
9697
10909
  publicKey: internalNode.signingPublicKey
9698
10910
  });
10911
+ const directFromCpfpRefundUserSignature = await this.config.signer.signFrost({
10912
+ message: directFromCpfpRefundTxSighash,
10913
+ publicKey: creationNode.directFromCpfpRefundTxSigningJob.signingPublicKey,
10914
+ keyDerivation,
10915
+ selfCommitment: creationNode.directFromCpfpRefundTxSigningCommitment,
10916
+ statechainCommitments: creationResponseNode.directFromCpfpRefundTxSigningResult?.signingNonceCommitments,
10917
+ verifyingKey: internalNode.verificationKey
10918
+ });
10919
+ directFromCpfpRefundTxSignature = await this.config.signer.aggregateFrost(
10920
+ {
10921
+ message: directFromCpfpRefundTxSighash,
10922
+ statechainSignatures: creationResponseNode.directFromCpfpRefundTxSigningResult?.signatureShares,
10923
+ statechainPublicKeys: creationResponseNode.directFromCpfpRefundTxSigningResult?.publicKeys,
10924
+ verifyingKey: internalNode.verificationKey,
10925
+ statechainCommitments: creationResponseNode.directFromCpfpRefundTxSigningResult?.signingNonceCommitments,
10926
+ selfCommitment: creationNode.directFromCpfpRefundTxSigningCommitment,
10927
+ selfSignature: directFromCpfpRefundUserSignature,
10928
+ publicKey: internalNode.signingPublicKey
10929
+ }
10930
+ );
9699
10931
  }
9700
10932
  return {
9701
- tx,
10933
+ tx: cpfpNodeTx,
10934
+ // Return CPFP version for children
9702
10935
  signature: {
9703
10936
  nodeId: creationResponseNode.nodeId,
9704
- nodeTxSignature,
9705
- refundTxSignature,
9706
- // TODO: Add direct refund signature
9707
- directNodeTxSignature: new Uint8Array(),
9708
- directRefundTxSignature: new Uint8Array(),
9709
- directFromCpfpRefundTxSignature: new Uint8Array()
10937
+ nodeTxSignature: cpfpNodeTxSignature,
10938
+ directNodeTxSignature,
10939
+ refundTxSignature: cpfpRefundTxSignature,
10940
+ directRefundTxSignature,
10941
+ directFromCpfpRefundTxSignature
9710
10942
  }
9711
10943
  };
9712
10944
  }
@@ -9768,8 +11000,45 @@ var SigningService = class {
9768
11000
  constructor(config) {
9769
11001
  this.config = config;
9770
11002
  }
9771
- async signRefunds(leaves, signingCommitments, receiverIdentityPubkey) {
9772
- const leafSigningJobs = [];
11003
+ async signRefundsInternal(refundTx, sighash, leaf, signingCommitments) {
11004
+ const leafSigningJobs = [];
11005
+ const signingCommitment = await this.config.signer.getRandomSigningCommitment();
11006
+ if (!signingCommitments) {
11007
+ throw new ValidationError("Invalid signing commitments", {
11008
+ field: "signingNonceCommitments",
11009
+ value: signingCommitments,
11010
+ expected: "Non-null signing commitments"
11011
+ });
11012
+ }
11013
+ const signingResult = await this.config.signer.signFrost({
11014
+ message: sighash,
11015
+ keyDerivation: leaf.keyDerivation,
11016
+ publicKey: await this.config.signer.getPublicKeyFromDerivation(
11017
+ leaf.keyDerivation
11018
+ ),
11019
+ selfCommitment: signingCommitment,
11020
+ statechainCommitments: signingCommitments,
11021
+ adaptorPubKey: new Uint8Array(),
11022
+ verifyingKey: leaf.leaf.verifyingPublicKey
11023
+ });
11024
+ leafSigningJobs.push({
11025
+ leafId: leaf.leaf.id,
11026
+ signingPublicKey: await this.config.signer.getPublicKeyFromDerivation(
11027
+ leaf.keyDerivation
11028
+ ),
11029
+ rawTx: refundTx.toBytes(),
11030
+ signingNonceCommitment: signingCommitment.commitment,
11031
+ userSignature: signingResult,
11032
+ signingCommitments: {
11033
+ signingCommitments
11034
+ }
11035
+ });
11036
+ return leafSigningJobs;
11037
+ }
11038
+ async signRefunds(leaves, receiverIdentityPubkey, cpfpSigningCommitments, directSigningCommitments, directFromCpfpSigningCommitments) {
11039
+ const cpfpLeafSigningJobs = [];
11040
+ const directLeafSigningJobs = [];
11041
+ const directFromCpfpLeafSigningJobs = [];
9773
11042
  for (let i = 0; i < leaves.length; i++) {
9774
11043
  const leaf = leaves[i];
9775
11044
  if (!leaf?.leaf) {
@@ -9780,14 +11049,20 @@ var SigningService = class {
9780
11049
  });
9781
11050
  }
9782
11051
  const nodeTx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
9783
- const nodeOutPoint = {
11052
+ const cpfpNodeOutPoint = {
9784
11053
  txid: hexToBytes11(getTxId(nodeTx)),
9785
11054
  index: 0
9786
11055
  };
9787
11056
  const currRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
9788
- const { nextSequence } = getNextTransactionSequence(
9789
- currRefundTx.getInput(0).sequence
9790
- );
11057
+ const sequence = currRefundTx.getInput(0).sequence;
11058
+ if (!sequence) {
11059
+ throw new ValidationError("Invalid refund transaction", {
11060
+ field: "sequence",
11061
+ value: currRefundTx.getInput(0),
11062
+ expected: "Non-null sequence"
11063
+ });
11064
+ }
11065
+ const { nextSequence, nextDirectSequence } = getNextTransactionSequence(sequence);
9791
11066
  const amountSats = currRefundTx.getOutput(0).amount;
9792
11067
  if (amountSats === void 0) {
9793
11068
  throw new ValidationError("Invalid refund transaction", {
@@ -9796,48 +11071,90 @@ var SigningService = class {
9796
11071
  expected: "Non-null amount"
9797
11072
  });
9798
11073
  }
9799
- const refundTx = createRefundTx(
9800
- nextSequence,
9801
- nodeOutPoint,
9802
- amountSats,
9803
- receiverIdentityPubkey,
9804
- this.config.getNetwork()
9805
- );
9806
- const sighash = getSigHashFromTx(refundTx, 0, nodeTx.getOutput(0));
9807
- const signingCommitment = await this.config.signer.getRandomSigningCommitment();
9808
- const signingNonceCommitments = signingCommitments[i]?.signingNonceCommitments;
9809
- if (!signingNonceCommitments) {
9810
- throw new ValidationError("Invalid signing commitments", {
9811
- field: "signingNonceCommitments",
9812
- value: signingCommitments[i],
9813
- expected: "Non-null signing nonce commitments"
9814
- });
11074
+ let directNodeTx;
11075
+ let directNodeOutPoint;
11076
+ if (leaf.leaf.directTx.length > 0) {
11077
+ directNodeTx = getTxFromRawTxBytes(leaf.leaf.directTx);
11078
+ directNodeOutPoint = {
11079
+ txid: hexToBytes11(getTxId(directNodeTx)),
11080
+ index: 0
11081
+ };
9815
11082
  }
9816
- const signingResult = await this.config.signer.signFrost({
9817
- message: sighash,
9818
- keyDerivation: leaf.keyDerivation,
9819
- publicKey: await this.config.signer.getPublicKeyFromDerivation(
9820
- leaf.keyDerivation
9821
- ),
9822
- selfCommitment: signingCommitment,
9823
- statechainCommitments: signingNonceCommitments,
9824
- adaptorPubKey: new Uint8Array(),
9825
- verifyingKey: leaf.leaf.verifyingPublicKey
11083
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
11084
+ sequence: nextSequence,
11085
+ directSequence: nextDirectSequence,
11086
+ input: cpfpNodeOutPoint,
11087
+ directInput: directNodeOutPoint,
11088
+ amountSats,
11089
+ receivingPubkey: receiverIdentityPubkey,
11090
+ network: this.config.getNetwork()
9826
11091
  });
9827
- leafSigningJobs.push({
9828
- leafId: leaf.leaf.id,
9829
- signingPublicKey: await this.config.signer.getPublicKeyFromDerivation(
9830
- leaf.keyDerivation
9831
- ),
9832
- rawTx: refundTx.toBytes(),
9833
- signingNonceCommitment: signingCommitment.commitment,
9834
- userSignature: signingResult,
9835
- signingCommitments: {
9836
- signingCommitments: signingNonceCommitments
11092
+ const refundSighash = getSigHashFromTx(
11093
+ cpfpRefundTx,
11094
+ 0,
11095
+ nodeTx.getOutput(0)
11096
+ );
11097
+ const signingJobs = await this.signRefundsInternal(
11098
+ cpfpRefundTx,
11099
+ refundSighash,
11100
+ leaf,
11101
+ cpfpSigningCommitments[i]?.signingNonceCommitments
11102
+ );
11103
+ cpfpLeafSigningJobs.push(...signingJobs);
11104
+ if (directRefundTx) {
11105
+ if (!directNodeTx) {
11106
+ throw new ValidationError(
11107
+ "Direct node transaction undefined while direct refund transaction is defined",
11108
+ {
11109
+ field: "directNodeTx",
11110
+ value: directNodeTx,
11111
+ expected: "Non-null direct node transaction"
11112
+ }
11113
+ );
9837
11114
  }
9838
- });
11115
+ const refundSighash2 = getSigHashFromTx(
11116
+ directRefundTx,
11117
+ 0,
11118
+ directNodeTx.getOutput(0)
11119
+ );
11120
+ const signingJobs2 = await this.signRefundsInternal(
11121
+ directRefundTx,
11122
+ refundSighash2,
11123
+ leaf,
11124
+ directSigningCommitments[i]?.signingNonceCommitments
11125
+ );
11126
+ directLeafSigningJobs.push(...signingJobs2);
11127
+ }
11128
+ if (directFromCpfpRefundTx) {
11129
+ if (!directNodeTx) {
11130
+ throw new ValidationError(
11131
+ "Direct node transaction undefined while direct from CPFP refund transaction is defined",
11132
+ {
11133
+ field: "directNodeTx",
11134
+ value: directNodeTx,
11135
+ expected: "Non-null direct node transaction"
11136
+ }
11137
+ );
11138
+ }
11139
+ const refundSighash2 = getSigHashFromTx(
11140
+ directFromCpfpRefundTx,
11141
+ 0,
11142
+ nodeTx.getOutput(0)
11143
+ );
11144
+ const signingJobs2 = await this.signRefundsInternal(
11145
+ directFromCpfpRefundTx,
11146
+ refundSighash2,
11147
+ leaf,
11148
+ directFromCpfpSigningCommitments[i]?.signingNonceCommitments
11149
+ );
11150
+ directFromCpfpLeafSigningJobs.push(...signingJobs2);
11151
+ }
9839
11152
  }
9840
- return leafSigningJobs;
11153
+ return {
11154
+ cpfpLeafSigningJobs,
11155
+ directLeafSigningJobs,
11156
+ directFromCpfpLeafSigningJobs
11157
+ };
9841
11158
  }
9842
11159
  };
9843
11160
 
@@ -10077,31 +11394,37 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
10077
11394
  }
10078
11395
  }
10079
11396
  async getLeaves(isBalanceCheck = false) {
10080
- const leaves = await this.queryNodes({
10081
- source: {
10082
- $case: "ownerIdentityPubkey",
10083
- ownerIdentityPubkey: await this.config.signer.getIdentityPublicKey()
10084
- },
10085
- includeParents: false,
10086
- network: NetworkToProto[this.config.getNetwork()]
10087
- });
11397
+ const operatorToLeaves = /* @__PURE__ */ new Map();
11398
+ const ownerIdentityPubkey = await this.config.signer.getIdentityPublicKey();
11399
+ let signingOperators = Object.entries(this.config.getSigningOperators());
11400
+ if (isBalanceCheck) {
11401
+ signingOperators = signingOperators.filter(
11402
+ ([id, _]) => id === this.config.getCoordinatorIdentifier()
11403
+ );
11404
+ }
11405
+ await Promise.all(
11406
+ signingOperators.map(async ([id, operator]) => {
11407
+ const leaves2 = await this.queryNodes(
11408
+ {
11409
+ source: {
11410
+ $case: "ownerIdentityPubkey",
11411
+ ownerIdentityPubkey
11412
+ },
11413
+ includeParents: false,
11414
+ network: NetworkToProto[this.config.getNetwork()]
11415
+ },
11416
+ operator.address
11417
+ );
11418
+ operatorToLeaves.set(id, leaves2);
11419
+ })
11420
+ );
11421
+ const leaves = operatorToLeaves.get(
11422
+ this.config.getCoordinatorIdentifier()
11423
+ );
10088
11424
  const leavesToIgnore = /* @__PURE__ */ new Set();
10089
11425
  if (!isBalanceCheck) {
10090
- for (const [id, operator] of Object.entries(
10091
- this.config.getSigningOperators()
10092
- )) {
11426
+ for (const [id, operatorLeaves] of operatorToLeaves) {
10093
11427
  if (id !== this.config.getCoordinatorIdentifier()) {
10094
- const operatorLeaves = await this.queryNodes(
10095
- {
10096
- source: {
10097
- $case: "ownerIdentityPubkey",
10098
- ownerIdentityPubkey: await this.config.signer.getIdentityPublicKey()
10099
- },
10100
- includeParents: false,
10101
- network: NetworkToProto[this.config.getNetwork()]
10102
- },
10103
- operator.address
10104
- );
10105
11428
  for (const [nodeId, leaf] of Object.entries(leaves.nodes)) {
10106
11429
  const operatorLeaf = operatorLeaves.nodes[nodeId];
10107
11430
  if (!operatorLeaf) {
@@ -10542,21 +11865,68 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
10542
11865
  }
10543
11866
  }))
10544
11867
  );
10545
- const { transfer, signatureMap } = await this.transferService.startSwapSignRefund(
11868
+ const {
11869
+ transfer,
11870
+ signatureMap,
11871
+ directSignatureMap,
11872
+ directFromCpfpSignatureMap
11873
+ } = await this.transferService.startSwapSignRefund(
10546
11874
  leafKeyTweaks,
10547
11875
  hexToBytes12(this.config.getSspIdentityPublicKey()),
10548
11876
  new Date(Date.now() + 2 * 60 * 1e3)
10549
11877
  );
10550
11878
  try {
10551
11879
  if (!transfer.leaves[0]?.leaf) {
11880
+ console.error("[processSwapBatch] First leaf is missing");
10552
11881
  throw new Error("Failed to get leaf");
10553
11882
  }
10554
- const refundSignature = signatureMap.get(transfer.leaves[0].leaf.id);
10555
- if (!refundSignature) {
10556
- throw new Error("Failed to get refund signature");
11883
+ const cpfpRefundSignature = signatureMap.get(transfer.leaves[0].leaf.id);
11884
+ if (!cpfpRefundSignature) {
11885
+ console.error(
11886
+ "[processSwapBatch] Missing CPFP refund signature for first leaf"
11887
+ );
11888
+ throw new Error("Failed to get CPFP refund signature");
11889
+ }
11890
+ const directRefundSignature = directSignatureMap.get(
11891
+ transfer.leaves[0].leaf.id
11892
+ );
11893
+ if (!directRefundSignature) {
11894
+ console.error(
11895
+ "[processSwapBatch] Missing direct refund signature for first leaf"
11896
+ );
11897
+ throw new Error("Failed to get direct refund signature");
11898
+ }
11899
+ const directFromCpfpRefundSignature = directFromCpfpSignatureMap.get(
11900
+ transfer.leaves[0].leaf.id
11901
+ );
11902
+ if (!directFromCpfpRefundSignature) {
11903
+ console.error(
11904
+ "[processSwapBatch] Missing direct from CPFP refund signature for first leaf"
11905
+ );
11906
+ throw new Error("Failed to get direct from CPFP refund signature");
11907
+ }
11908
+ const {
11909
+ adaptorPrivateKey: cpfpAdaptorPrivateKey,
11910
+ adaptorSignature: cpfpAdaptorSignature
11911
+ } = generateAdaptorFromSignature(cpfpRefundSignature);
11912
+ let directAdaptorPrivateKey = new Uint8Array();
11913
+ let directAdaptorSignature = new Uint8Array();
11914
+ let directFromCpfpAdaptorPrivateKey = new Uint8Array();
11915
+ let directFromCpfpAdaptorSignature = new Uint8Array();
11916
+ if (directRefundSignature.length > 0) {
11917
+ const { adaptorPrivateKey, adaptorSignature } = generateAdaptorFromSignature(directRefundSignature);
11918
+ directAdaptorPrivateKey = adaptorPrivateKey;
11919
+ directAdaptorSignature = adaptorSignature;
11920
+ }
11921
+ if (directFromCpfpRefundSignature.length > 0) {
11922
+ const { adaptorPrivateKey, adaptorSignature } = generateAdaptorFromSignature(directFromCpfpRefundSignature);
11923
+ directFromCpfpAdaptorPrivateKey = adaptorPrivateKey;
11924
+ directFromCpfpAdaptorSignature = adaptorSignature;
10557
11925
  }
10558
- const { adaptorPrivateKey, adaptorSignature } = generateAdaptorFromSignature(refundSignature);
10559
11926
  if (!transfer.leaves[0].leaf) {
11927
+ console.error(
11928
+ "[processSwapBatch] First leaf missing when preparing user leaves"
11929
+ );
10560
11930
  throw new Error("Failed to get leaf");
10561
11931
  }
10562
11932
  const userLeaves = [];
@@ -10565,37 +11935,113 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
10565
11935
  raw_unsigned_refund_transaction: bytesToHex11(
10566
11936
  transfer.leaves[0].intermediateRefundTx
10567
11937
  ),
10568
- adaptor_added_signature: bytesToHex11(adaptorSignature)
11938
+ direct_raw_unsigned_refund_transaction: bytesToHex11(
11939
+ transfer.leaves[0].intermediateDirectRefundTx
11940
+ ),
11941
+ direct_from_cpfp_raw_unsigned_refund_transaction: bytesToHex11(
11942
+ transfer.leaves[0].intermediateDirectFromCpfpRefundTx
11943
+ ),
11944
+ adaptor_added_signature: bytesToHex11(cpfpAdaptorSignature),
11945
+ direct_adaptor_added_signature: bytesToHex11(directAdaptorSignature),
11946
+ direct_from_cpfp_adaptor_added_signature: bytesToHex11(
11947
+ directFromCpfpAdaptorSignature
11948
+ )
10569
11949
  });
10570
11950
  for (let i = 1; i < transfer.leaves.length; i++) {
10571
11951
  const leaf = transfer.leaves[i];
10572
11952
  if (!leaf?.leaf) {
11953
+ console.error(`[processSwapBatch] Leaf ${i + 1} is missing`);
10573
11954
  throw new Error("Failed to get leaf");
10574
11955
  }
10575
- const refundSignature2 = signatureMap.get(leaf.leaf.id);
10576
- if (!refundSignature2) {
10577
- throw new Error("Failed to get refund signature");
11956
+ const cpfpRefundSignature2 = signatureMap.get(leaf.leaf.id);
11957
+ if (!cpfpRefundSignature2) {
11958
+ console.error(
11959
+ `[processSwapBatch] Missing CPFP refund signature for leaf ${i + 1}`
11960
+ );
11961
+ throw new Error("Failed to get CPFP refund signature");
11962
+ }
11963
+ const directRefundSignature2 = directSignatureMap.get(leaf.leaf.id);
11964
+ if (!directRefundSignature2) {
11965
+ console.error(
11966
+ `[processSwapBatch] Missing direct refund signature for leaf ${i + 1}`
11967
+ );
11968
+ throw new Error("Failed to get direct refund signature");
11969
+ }
11970
+ const directFromCpfpRefundSignature2 = directFromCpfpSignatureMap.get(
11971
+ leaf.leaf.id
11972
+ );
11973
+ if (!directFromCpfpRefundSignature2) {
11974
+ console.error(
11975
+ `[processSwapBatch] Missing direct from CPFP refund signature for leaf ${i + 1}`
11976
+ );
11977
+ throw new Error("Failed to get direct from CPFP refund signature");
10578
11978
  }
10579
- const signature = generateSignatureFromExistingAdaptor(
10580
- refundSignature2,
10581
- adaptorPrivateKey
11979
+ const cpfpSignature = generateSignatureFromExistingAdaptor(
11980
+ cpfpRefundSignature2,
11981
+ cpfpAdaptorPrivateKey
10582
11982
  );
11983
+ let directSignature = new Uint8Array();
11984
+ if (directRefundSignature2.length > 0) {
11985
+ directSignature = generateSignatureFromExistingAdaptor(
11986
+ directRefundSignature2,
11987
+ directAdaptorPrivateKey
11988
+ );
11989
+ }
11990
+ let directFromCpfpSignature = new Uint8Array();
11991
+ if (directFromCpfpRefundSignature2.length > 0) {
11992
+ directFromCpfpSignature = generateSignatureFromExistingAdaptor(
11993
+ directFromCpfpRefundSignature2,
11994
+ directFromCpfpAdaptorPrivateKey
11995
+ );
11996
+ }
10583
11997
  userLeaves.push({
10584
11998
  leaf_id: leaf.leaf.id,
10585
11999
  raw_unsigned_refund_transaction: bytesToHex11(
10586
12000
  leaf.intermediateRefundTx
10587
12001
  ),
10588
- adaptor_added_signature: bytesToHex11(signature)
12002
+ direct_raw_unsigned_refund_transaction: bytesToHex11(
12003
+ leaf.intermediateDirectRefundTx
12004
+ ),
12005
+ direct_from_cpfp_raw_unsigned_refund_transaction: bytesToHex11(
12006
+ leaf.intermediateDirectFromCpfpRefundTx
12007
+ ),
12008
+ adaptor_added_signature: bytesToHex11(cpfpSignature),
12009
+ direct_adaptor_added_signature: bytesToHex11(directSignature),
12010
+ direct_from_cpfp_adaptor_added_signature: bytesToHex11(
12011
+ directFromCpfpSignature
12012
+ )
10589
12013
  });
10590
12014
  }
10591
12015
  const sspClient = this.getSspClient();
10592
- const adaptorPubkey = bytesToHex11(
10593
- secp256k114.getPublicKey(adaptorPrivateKey)
12016
+ const cpfpAdaptorPubkey = bytesToHex11(
12017
+ secp256k114.getPublicKey(cpfpAdaptorPrivateKey)
10594
12018
  );
12019
+ if (!cpfpAdaptorPubkey) {
12020
+ throw new Error("Failed to generate CPFP adaptor pubkey");
12021
+ }
12022
+ let directAdaptorPubkey;
12023
+ if (directAdaptorPrivateKey.length > 0) {
12024
+ directAdaptorPubkey = bytesToHex11(
12025
+ secp256k114.getPublicKey(directAdaptorPrivateKey)
12026
+ );
12027
+ }
12028
+ let directFromCpfpAdaptorPubkey;
12029
+ if (directFromCpfpAdaptorPrivateKey.length > 0) {
12030
+ directFromCpfpAdaptorPubkey = bytesToHex11(
12031
+ secp256k114.getPublicKey(directFromCpfpAdaptorPrivateKey)
12032
+ );
12033
+ }
10595
12034
  let request = null;
12035
+ const targetAmountSats = targetAmounts?.reduce((acc, amount) => acc + amount, 0) || leavesBatch.reduce((acc, leaf) => acc + leaf.value, 0);
12036
+ const totalAmountSats = leavesBatch.reduce(
12037
+ (acc, leaf) => acc + leaf.value,
12038
+ 0
12039
+ );
10596
12040
  request = await sspClient.requestLeaveSwap({
10597
12041
  userLeaves,
10598
- adaptorPubkey,
12042
+ adaptorPubkey: cpfpAdaptorPubkey,
12043
+ directAdaptorPubkey,
12044
+ directFromCpfpAdaptorPubkey,
10599
12045
  targetAmountSats: targetAmounts?.reduce((acc, amount) => acc + amount, 0) || leavesBatch.reduce((acc, leaf) => acc + leaf.value, 0),
10600
12046
  totalAmountSats: leavesBatch.reduce((acc, leaf) => acc + leaf.value, 0),
10601
12047
  targetAmountSatsList: targetAmounts,
@@ -10604,6 +12050,7 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
10604
12050
  idempotencyKey: uuidv75()
10605
12051
  });
10606
12052
  if (!request) {
12053
+ console.error("[processSwapBatch] Leave swap request returned null");
10607
12054
  throw new Error("Failed to request leaves swap. No response returned.");
10608
12055
  }
10609
12056
  const nodes = await this.queryNodes({
@@ -10617,50 +12064,140 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
10617
12064
  network: NetworkToProto[this.config.getNetwork()]
10618
12065
  });
10619
12066
  if (Object.values(nodes.nodes).length !== request.swapLeaves.length) {
12067
+ console.error("[processSwapBatch] Node count mismatch:", {
12068
+ actual: Object.values(nodes.nodes).length,
12069
+ expected: request.swapLeaves.length
12070
+ });
10620
12071
  throw new Error("Expected same number of nodes as swapLeaves");
10621
12072
  }
10622
12073
  for (const [nodeId, node] of Object.entries(nodes.nodes)) {
10623
12074
  if (!node.nodeTx) {
12075
+ console.error(`[processSwapBatch] Node tx missing for ${nodeId}`);
10624
12076
  throw new Error(`Node tx not found for leaf ${nodeId}`);
10625
12077
  }
10626
12078
  if (!node.verifyingPublicKey) {
12079
+ console.error(
12080
+ `[processSwapBatch] Verifying public key missing for ${nodeId}`
12081
+ );
10627
12082
  throw new Error(`Node public key not found for leaf ${nodeId}`);
10628
12083
  }
10629
12084
  const leaf = request.swapLeaves.find((leaf2) => leaf2.leafId === nodeId);
10630
12085
  if (!leaf) {
12086
+ console.error(`[processSwapBatch] Leaf not found for node ${nodeId}`);
10631
12087
  throw new Error(`Leaf not found for node ${nodeId}`);
10632
12088
  }
10633
- const nodeTx = getTxFromRawTxBytes(node.nodeTx);
10634
- const refundTxBytes = hexToBytes12(leaf.rawUnsignedRefundTransaction);
10635
- const refundTx = getTxFromRawTxBytes(refundTxBytes);
10636
- const sighash = getSigHashFromTx(refundTx, 0, nodeTx.getOutput(0));
12089
+ const cpfpNodeTx = getTxFromRawTxBytes(node.nodeTx);
12090
+ const cpfpRefundTxBytes = hexToBytes12(leaf.rawUnsignedRefundTransaction);
12091
+ const cpfpRefundTx = getTxFromRawTxBytes(cpfpRefundTxBytes);
12092
+ const cpfpSighash = getSigHashFromTx(
12093
+ cpfpRefundTx,
12094
+ 0,
12095
+ cpfpNodeTx.getOutput(0)
12096
+ );
10637
12097
  const nodePublicKey = node.verifyingPublicKey;
10638
12098
  const taprootKey = computeTaprootKeyNoScript(nodePublicKey.slice(1));
10639
- const adaptorSignatureBytes = hexToBytes12(leaf.adaptorSignedSignature);
12099
+ const cpfpAdaptorSignatureBytes = hexToBytes12(
12100
+ leaf.adaptorSignedSignature
12101
+ );
12102
+ applyAdaptorToSignature(
12103
+ taprootKey.slice(1),
12104
+ cpfpSighash,
12105
+ cpfpAdaptorSignatureBytes,
12106
+ cpfpAdaptorPrivateKey
12107
+ );
12108
+ if (!leaf.directRawUnsignedRefundTransaction) {
12109
+ throw new Error(
12110
+ `Direct raw unsigned refund transaction missing for node ${nodeId}`
12111
+ );
12112
+ }
12113
+ if (!leaf.directAdaptorSignedSignature) {
12114
+ throw new Error(
12115
+ `Direct adaptor signed signature missing for node ${nodeId}`
12116
+ );
12117
+ }
12118
+ const directNodeTx = getTxFromRawTxBytes(node.directTx);
12119
+ const directRefundTxBytes = hexToBytes12(
12120
+ leaf.directRawUnsignedRefundTransaction
12121
+ );
12122
+ const directRefundTx = getTxFromRawTxBytes(directRefundTxBytes);
12123
+ const directSighash = getSigHashFromTx(
12124
+ directRefundTx,
12125
+ 0,
12126
+ directNodeTx.getOutput(0)
12127
+ );
12128
+ if (!leaf.directFromCpfpAdaptorSignedSignature) {
12129
+ throw new Error(
12130
+ `Direct adaptor signed signature missing for node ${nodeId}`
12131
+ );
12132
+ }
12133
+ const directAdaptorSignatureBytes = hexToBytes12(
12134
+ leaf.directAdaptorSignedSignature
12135
+ );
12136
+ applyAdaptorToSignature(
12137
+ taprootKey.slice(1),
12138
+ directSighash,
12139
+ directAdaptorSignatureBytes,
12140
+ directAdaptorPrivateKey
12141
+ );
12142
+ if (!leaf.directRawUnsignedRefundTransaction) {
12143
+ throw new Error(
12144
+ `Direct raw unsigned refund transaction missing for node ${nodeId}`
12145
+ );
12146
+ }
12147
+ if (!leaf.directFromCpfpRawUnsignedRefundTransaction) {
12148
+ throw new Error(
12149
+ `Direct raw unsigned refund transaction missing for node ${nodeId}`
12150
+ );
12151
+ }
12152
+ const directFromCpfpRefundTxBytes = hexToBytes12(
12153
+ leaf.directFromCpfpRawUnsignedRefundTransaction
12154
+ );
12155
+ const directFromCpfpRefundTx = getTxFromRawTxBytes(
12156
+ directFromCpfpRefundTxBytes
12157
+ );
12158
+ const directFromCpfpSighash = getSigHashFromTx(
12159
+ directFromCpfpRefundTx,
12160
+ 0,
12161
+ cpfpNodeTx.getOutput(0)
12162
+ );
12163
+ const directFromCpfpAdaptorSignatureBytes = hexToBytes12(
12164
+ leaf.directFromCpfpAdaptorSignedSignature
12165
+ );
10640
12166
  applyAdaptorToSignature(
10641
12167
  taprootKey.slice(1),
10642
- sighash,
10643
- adaptorSignatureBytes,
10644
- adaptorPrivateKey
12168
+ directFromCpfpSighash,
12169
+ directFromCpfpAdaptorSignatureBytes,
12170
+ directFromCpfpAdaptorPrivateKey
10645
12171
  );
10646
12172
  }
10647
12173
  await this.transferService.deliverTransferPackage(
10648
12174
  transfer,
10649
12175
  leafKeyTweaks,
10650
- signatureMap
12176
+ signatureMap,
12177
+ directSignatureMap,
12178
+ directFromCpfpSignatureMap
10651
12179
  );
10652
12180
  const completeResponse = await sspClient.completeLeaveSwap({
10653
- adaptorSecretKey: bytesToHex11(adaptorPrivateKey),
12181
+ adaptorSecretKey: bytesToHex11(cpfpAdaptorPrivateKey),
12182
+ directAdaptorSecretKey: bytesToHex11(directAdaptorPrivateKey),
12183
+ directFromCpfpAdaptorSecretKey: bytesToHex11(
12184
+ directFromCpfpAdaptorPrivateKey
12185
+ ),
10654
12186
  userOutboundTransferExternalId: transfer.id,
10655
12187
  leavesSwapRequestId: request.id
10656
12188
  });
10657
12189
  if (!completeResponse || !completeResponse.inboundTransfer?.sparkId) {
12190
+ console.error(
12191
+ "[processSwapBatch] Invalid complete response:",
12192
+ completeResponse
12193
+ );
10658
12194
  throw new Error("Failed to complete leaves swap");
10659
12195
  }
10660
12196
  const incomingTransfer = await this.transferService.queryTransfer(
10661
12197
  completeResponse.inboundTransfer.sparkId
10662
12198
  );
10663
12199
  if (!incomingTransfer) {
12200
+ console.error("[processSwapBatch] No incoming transfer found");
10664
12201
  throw new Error("Failed to get incoming transfer");
10665
12202
  }
10666
12203
  return await this.claimTransfer({
@@ -10670,6 +12207,11 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
10670
12207
  optimize: false
10671
12208
  });
10672
12209
  } catch (e) {
12210
+ console.error("[processSwapBatch] Error details:", {
12211
+ error: e,
12212
+ message: e.message,
12213
+ stack: e.stack
12214
+ });
10673
12215
  await this.cancelAllSenderInitiatedTransfers();
10674
12216
  throw new Error(`Failed to request leaves swap: ${e}`);
10675
12217
  }
@@ -10689,9 +12231,22 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
10689
12231
  const identityPublicKey = bytesToHex11(
10690
12232
  await this.config.signer.getIdentityPublicKey()
10691
12233
  );
12234
+ const userRequests = await this.sspClient?.getTransfers(
12235
+ transfers.transfers.map((transfer) => transfer.id)
12236
+ );
12237
+ const userRequestsMap = /* @__PURE__ */ new Map();
12238
+ for (const userRequest of userRequests || []) {
12239
+ if (userRequest && userRequest.sparkId && userRequest.userRequest) {
12240
+ userRequestsMap.set(userRequest.sparkId, userRequest.userRequest);
12241
+ }
12242
+ }
10692
12243
  return {
10693
12244
  transfers: transfers.transfers.map(
10694
- (transfer) => mapTransferToWalletTransfer(transfer, identityPublicKey)
12245
+ (transfer) => mapTransferToWalletTransfer(
12246
+ transfer,
12247
+ identityPublicKey,
12248
+ userRequestsMap.get(transfer.id)
12249
+ )
10695
12250
  ),
10696
12251
  offset: transfers.offset
10697
12252
  };
@@ -10874,7 +12429,9 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
10874
12429
  network = BitcoinNetwork_default.REGTEST;
10875
12430
  }
10876
12431
  if (outputIndex === void 0) {
10877
- outputIndex = await this.getDepositTransactionVout(transactionId);
12432
+ outputIndex = await this.getDepositTransactionVout({
12433
+ txid: transactionId
12434
+ });
10878
12435
  }
10879
12436
  const quote = await sspClient.getClaimDepositQuote({
10880
12437
  transactionId,
@@ -10905,7 +12462,9 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
10905
12462
  throw new Error("SSP client not initialized");
10906
12463
  }
10907
12464
  if (outputIndex === void 0) {
10908
- outputIndex = await this.getDepositTransactionVout(transactionId);
12465
+ outputIndex = await this.getDepositTransactionVout({
12466
+ txid: transactionId
12467
+ });
10909
12468
  }
10910
12469
  let network = this.config.getSspNetwork();
10911
12470
  if (network === BitcoinNetwork_default.FUTURE_VALUE) {
@@ -10939,25 +12498,94 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
10939
12498
  }
10940
12499
  return response;
10941
12500
  }
12501
+ /**
12502
+ * Get a quote on how much credit you can claim for a deposit from the SSP. If the quote charges less fees than the max fee, claim the deposit.
12503
+ *
12504
+ * @param {Object} params - The parameters object
12505
+ * @param {string} params.transactionId - The ID of the transaction
12506
+ * @param {number} params.maxFee - The maximum fee to claim the deposit for
12507
+ * @param {number} [params.outputIndex] - The index of the output
12508
+ * @returns {Promise<StaticDepositQuoteOutput>} Quote for claiming a deposit to a static deposit address
12509
+ */
12510
+ async claimStaticDepositWithMaxFee({
12511
+ transactionId,
12512
+ maxFee,
12513
+ outputIndex
12514
+ }) {
12515
+ const sspClient = this.getSspClient();
12516
+ let network = this.config.getSspNetwork();
12517
+ if (network === BitcoinNetwork_default.FUTURE_VALUE) {
12518
+ network = BitcoinNetwork_default.REGTEST;
12519
+ }
12520
+ const depositTx = await this.getDepositTransaction(transactionId);
12521
+ if (outputIndex === void 0) {
12522
+ outputIndex = await this.getDepositTransactionVout({
12523
+ txid: transactionId,
12524
+ depositTx
12525
+ });
12526
+ }
12527
+ const depositAmount = Number(depositTx.getOutput(outputIndex).amount);
12528
+ const quote = await sspClient.getClaimDepositQuote({
12529
+ transactionId,
12530
+ outputIndex,
12531
+ network
12532
+ });
12533
+ if (!quote) {
12534
+ throw new Error("Failed to get claim deposit quote");
12535
+ }
12536
+ const { creditAmountSats, signature: sspSignature } = quote;
12537
+ const feeCharged = depositAmount - creditAmountSats;
12538
+ if (feeCharged > maxFee) {
12539
+ throw new ValidationError("Fee larger than max fee", {
12540
+ field: "feeCharged",
12541
+ value: feeCharged
12542
+ });
12543
+ }
12544
+ const response = await this.claimStaticDeposit({
12545
+ transactionId,
12546
+ creditAmountSats,
12547
+ sspSignature,
12548
+ outputIndex
12549
+ });
12550
+ if (!response) {
12551
+ throw new Error("Failed to claim static deposit");
12552
+ }
12553
+ return response;
12554
+ }
10942
12555
  /**
10943
12556
  * Refunds a static deposit to a destination address.
10944
12557
  *
10945
- * @param {string} depositTransactionId - The ID of the transaction
10946
- * @param {number} [outputIndex] - The index of the output
10947
- * @param {string} destinationAddress - The destination address
10948
- * @param {number} fee - The fee to refund
12558
+ * @param {Object} params - The refund parameters
12559
+ * @param {string} params.depositTransactionId - The ID of the transaction
12560
+ * @param {number} [params.outputIndex] - The index of the output
12561
+ * @param {string} params.destinationAddress - The destination address
12562
+ * @param {number} [params.fee] - **@deprecated** The fee to refund
12563
+ * @param {number} [params.satsPerVbyteFee] - The fee per vbyte to refund
10949
12564
  * @returns {Promise<string>} The hex of the refund transaction
10950
12565
  */
10951
12566
  async refundStaticDeposit({
10952
12567
  depositTransactionId,
10953
12568
  outputIndex,
10954
12569
  destinationAddress,
10955
- fee
12570
+ fee,
12571
+ satsPerVbyteFee
10956
12572
  }) {
10957
- if (fee <= 300) {
12573
+ if (fee !== void 0) {
12574
+ console.warn(
12575
+ `refundStaticDeposit(): \`fee\` parameter is deprecated and will be removed; please switch to \`satsPerVbyteFee\`.`
12576
+ );
12577
+ }
12578
+ if (fee === void 0 && satsPerVbyteFee === void 0) {
12579
+ throw new ValidationError("Fee or satsPerVbyteFee must be provided");
12580
+ }
12581
+ if (satsPerVbyteFee && satsPerVbyteFee > 150) {
12582
+ throw new ValidationError("satsPerVbyteFee must be less than 150");
12583
+ }
12584
+ const finalFee = satsPerVbyteFee ? satsPerVbyteFee * getTxEstimatedVbytesSizeByNumberOfInputsOutputs(1, 1) : fee;
12585
+ if (finalFee <= 200) {
10958
12586
  throw new ValidationError("Fee must be greater than 300", {
10959
12587
  field: "fee",
10960
- value: fee
12588
+ value: finalFee
10961
12589
  });
10962
12590
  }
10963
12591
  let network = this.config.getNetwork();
@@ -10965,10 +12593,13 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
10965
12593
  const networkJSON = networkToJSON(networkType);
10966
12594
  const depositTx = await this.getDepositTransaction(depositTransactionId);
10967
12595
  if (outputIndex === void 0) {
10968
- outputIndex = await this.getDepositTransactionVout(depositTransactionId);
12596
+ outputIndex = await this.getDepositTransactionVout({
12597
+ txid: depositTransactionId,
12598
+ depositTx
12599
+ });
10969
12600
  }
10970
12601
  const totalAmount = depositTx.getOutput(outputIndex).amount;
10971
- const creditAmountSats = Number(totalAmount) - fee;
12602
+ const creditAmountSats = Number(totalAmount) - finalFee;
10972
12603
  if (creditAmountSats <= 0) {
10973
12604
  throw new ValidationError(
10974
12605
  "Fee too large. Credit amount must be greater than 0",
@@ -10984,10 +12615,10 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
10984
12615
  index: outputIndex,
10985
12616
  witnessScript: new Uint8Array()
10986
12617
  });
10987
- const addressDecoded = Address5(getNetwork(network)).decode(
12618
+ const addressDecoded = Address4(getNetwork(network)).decode(
10988
12619
  destinationAddress
10989
12620
  );
10990
- const outputScript = OutScript5.encode(addressDecoded);
12621
+ const outputScript = OutScript4.encode(addressDecoded);
10991
12622
  tx.addOutput({
10992
12623
  script: outputScript,
10993
12624
  amount: BigInt(creditAmountSats)
@@ -11016,26 +12647,14 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
11016
12647
  const sparkClient = await this.connectionManager.createSparkClient(
11017
12648
  this.config.getCoordinatorAddress()
11018
12649
  );
11019
- const transferId = uuidv75();
11020
- const swapResponse = await sparkClient.initiate_utxo_swap({
12650
+ const swapResponse = await sparkClient.initiate_static_deposit_utxo_refund({
11021
12651
  onChainUtxo: {
11022
12652
  txid: hexToBytes12(depositTransactionId),
11023
12653
  vout: outputIndex,
11024
12654
  network: networkType
11025
12655
  },
11026
- requestType: 2 /* Refund */,
11027
- amount: {
11028
- creditAmountSats: 0,
11029
- $case: "creditAmountSats"
11030
- },
11031
12656
  userSignature: swapResponseUserSignature,
11032
- sspSignature: new Uint8Array(),
11033
- transfer: {
11034
- transferId,
11035
- ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
11036
- receiverIdentityPublicKey: await this.config.signer.getIdentityPublicKey()
11037
- },
11038
- spendTxSigningJob: signingJob
12657
+ refundTxSigningJob: signingJob
11039
12658
  });
11040
12659
  if (!swapResponse) {
11041
12660
  throw new Error("Failed to initiate utxo swap");
@@ -11048,15 +12667,15 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
11048
12667
  path: 0
11049
12668
  },
11050
12669
  selfCommitment: signingNonceCommitment,
11051
- statechainCommitments: swapResponse.spendTxSigningResult.signingNonceCommitments,
12670
+ statechainCommitments: swapResponse.refundTxSigningResult.signingNonceCommitments,
11052
12671
  verifyingKey: swapResponse.depositAddress.verifyingPublicKey
11053
12672
  });
11054
12673
  const signatureResult = await this.config.signer.aggregateFrost({
11055
12674
  message: spendTxSighash,
11056
- statechainSignatures: swapResponse.spendTxSigningResult.signatureShares,
11057
- statechainPublicKeys: swapResponse.spendTxSigningResult.publicKeys,
12675
+ statechainSignatures: swapResponse.refundTxSigningResult.signatureShares,
12676
+ statechainPublicKeys: swapResponse.refundTxSigningResult.publicKeys,
11058
12677
  verifyingKey: swapResponse.depositAddress.verifyingPublicKey,
11059
- statechainCommitments: swapResponse.spendTxSigningResult.signingNonceCommitments,
12678
+ statechainCommitments: swapResponse.refundTxSigningResult.signingNonceCommitments,
11060
12679
  selfCommitment: signingNonceCommitment,
11061
12680
  publicKey: await this.config.signer.getStaticDepositSigningKey(0),
11062
12681
  selfSignature: userSignature
@@ -11109,8 +12728,13 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
11109
12728
  }
11110
12729
  return payload;
11111
12730
  }
11112
- async getDepositTransactionVout(txid) {
11113
- const depositTx = await this.getDepositTransaction(txid);
12731
+ async getDepositTransactionVout({
12732
+ txid,
12733
+ depositTx
12734
+ }) {
12735
+ if (!depositTx) {
12736
+ depositTx = await this.getDepositTransaction(txid);
12737
+ }
11114
12738
  const staticDepositAddresses = new Set(
11115
12739
  await this.queryStaticDepositAddresses()
11116
12740
  );
@@ -11120,8 +12744,8 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
11120
12744
  if (!output) {
11121
12745
  continue;
11122
12746
  }
11123
- const parsedScript = OutScript5.decode(output.script);
11124
- const address2 = Address5(getNetwork(this.config.getNetwork())).encode(
12747
+ const parsedScript = OutScript4.decode(output.script);
12748
+ const address2 = Address4(getNetwork(this.config.getNetwork())).encode(
11125
12749
  parsedScript
11126
12750
  );
11127
12751
  if (staticDepositAddresses.has(address2)) {
@@ -11140,8 +12764,9 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
11140
12764
  field: "txid"
11141
12765
  });
11142
12766
  }
12767
+ const { fetch: fetch2, Headers: Headers2 } = getFetch();
11143
12768
  const baseUrl = this.config.getElectrsUrl();
11144
- const headers = {};
12769
+ const headers = new Headers2();
11145
12770
  let txHex;
11146
12771
  if (this.config.getNetwork() === 4 /* LOCAL */) {
11147
12772
  const localFaucet = BitcoinFaucet.getInstance();
@@ -11152,9 +12777,9 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
11152
12777
  const auth = btoa(
11153
12778
  `${ELECTRS_CREDENTIALS.username}:${ELECTRS_CREDENTIALS.password}`
11154
12779
  );
11155
- headers["Authorization"] = `Basic ${auth}`;
12780
+ headers.set("Authorization", `Basic ${auth}`);
11156
12781
  }
11157
- const response = await fetch(`${baseUrl}/tx/${txid}/hex`, {
12782
+ const response = await fetch2(`${baseUrl}/tx/${txid}/hex`, {
11158
12783
  headers
11159
12784
  });
11160
12785
  txHex = await response.text();
@@ -11282,8 +12907,9 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
11282
12907
  this.mutexes.set(txid, mutex);
11283
12908
  }
11284
12909
  const nodes = await mutex.runExclusive(async () => {
12910
+ const { fetch: fetch2, Headers: Headers2 } = getFetch();
11285
12911
  const baseUrl = this.config.getElectrsUrl();
11286
- const headers = {};
12912
+ const headers = new Headers2();
11287
12913
  let txHex;
11288
12914
  if (this.config.getNetwork() === 4 /* LOCAL */) {
11289
12915
  const localFaucet = BitcoinFaucet.getInstance();
@@ -11294,9 +12920,9 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
11294
12920
  const auth = btoa(
11295
12921
  `${ELECTRS_CREDENTIALS.username}:${ELECTRS_CREDENTIALS.password}`
11296
12922
  );
11297
- headers["Authorization"] = `Basic ${auth}`;
12923
+ headers.set("Authorization", `Basic ${auth}`);
11298
12924
  }
11299
- const response = await fetch(`${baseUrl}/tx/${txid}/hex`, {
12925
+ const response = await fetch2(`${baseUrl}/tx/${txid}/hex`, {
11300
12926
  headers
11301
12927
  });
11302
12928
  txHex = await response.text();
@@ -11324,8 +12950,8 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
11324
12950
  if (!output) {
11325
12951
  continue;
11326
12952
  }
11327
- const parsedScript = OutScript5.decode(output.script);
11328
- const address2 = Address5(getNetwork(this.config.getNetwork())).encode(
12953
+ const parsedScript = OutScript4.decode(output.script);
12954
+ const address2 = Address4(getNetwork(this.config.getNetwork())).encode(
11329
12955
  parsedScript
11330
12956
  );
11331
12957
  if (unusedDepositAddresses.has(address2)) {
@@ -11385,8 +13011,8 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
11385
13011
  if (!output) {
11386
13012
  continue;
11387
13013
  }
11388
- const parsedScript = OutScript5.decode(output.script);
11389
- const address2 = Address5(getNetwork(this.config.getNetwork())).encode(
13014
+ const parsedScript = OutScript4.decode(output.script);
13015
+ const address2 = Address4(getNetwork(this.config.getNetwork())).encode(
11390
13016
  parsedScript
11391
13017
  );
11392
13018
  const unusedDepositAddress = unusedDepositAddresses.get(address2);
@@ -11538,10 +13164,16 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
11538
13164
  const validNodes = [];
11539
13165
  for (const node of nodes) {
11540
13166
  const nodeTx = getTxFromRawTxBytes(node.nodeTx);
11541
- const { needRefresh } = getNextTransactionSequence(
11542
- nodeTx.getInput(0).sequence
11543
- );
11544
- if (needRefresh) {
13167
+ const sequence = nodeTx.getInput(0).sequence;
13168
+ if (!sequence) {
13169
+ throw new ValidationError("Invalid node transaction", {
13170
+ field: "sequence",
13171
+ value: nodeTx.getInput(0),
13172
+ expected: "Non-null sequence"
13173
+ });
13174
+ }
13175
+ const needsRefresh = doesLeafNeedRefresh(sequence, true);
13176
+ if (needsRefresh) {
11545
13177
  nodesToExtend.push(node);
11546
13178
  nodeIds.push(node.id);
11547
13179
  } else {
@@ -11578,11 +13210,16 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
11578
13210
  const validNodes = [];
11579
13211
  for (const node of nodes) {
11580
13212
  const refundTx = getTxFromRawTxBytes(node.refundTx);
11581
- const { needRefresh } = getNextTransactionSequence(
11582
- refundTx.getInput(0).sequence,
11583
- true
11584
- );
11585
- if (needRefresh) {
13213
+ const sequence = refundTx.getInput(0).sequence;
13214
+ if (!sequence) {
13215
+ throw new ValidationError("Invalid refund transaction", {
13216
+ field: "sequence",
13217
+ value: refundTx.getInput(0),
13218
+ expected: "Non-null sequence"
13219
+ });
13220
+ }
13221
+ const needsRefresh = doesLeafNeedRefresh(sequence);
13222
+ if (needsRefresh) {
11586
13223
  nodesToRefresh.push(node);
11587
13224
  nodeIds.push(node.id);
11588
13225
  } else {
@@ -11616,7 +13253,7 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
11616
13253
  throw new Error(`parent node ${node.parentNodeId} not found`);
11617
13254
  }
11618
13255
  const { nodes: nodes2 } = await this.transferService.refreshTimelockNodes(
11619
- [node],
13256
+ node,
11620
13257
  parentNode
11621
13258
  );
11622
13259
  if (nodes2.length !== 1) {
@@ -11665,7 +13302,9 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
11665
13302
  leavesToClaim.push({
11666
13303
  leaf: {
11667
13304
  ...leaf.leaf,
11668
- refundTx: leaf.intermediateRefundTx
13305
+ refundTx: leaf.intermediateRefundTx,
13306
+ directRefundTx: leaf.intermediateDirectRefundTx,
13307
+ directFromCpfpRefundTx: leaf.intermediateDirectFromCpfpRefundTx
11669
13308
  },
11670
13309
  keyDerivation: {
11671
13310
  type: "ecies" /* ECIES */,
@@ -11741,7 +13380,7 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
11741
13380
  if (type && transfer.type !== type) {
11742
13381
  continue;
11743
13382
  }
11744
- if (transfer.status !== 2 /* TRANSFER_STATUS_SENDER_KEY_TWEAKED */ && transfer.status !== 3 /* TRANSFER_STATUS_RECEIVER_KEY_TWEAKED */ && transfer.status !== 4 /* TRANSFER_STATUS_RECEIVER_REFUND_SIGNED */ && transfer.status !== 10 /* TRANSFER_STATUS_RECEIVER_KEY_TWEAK_APPLIED */) {
13383
+ if (transfer.status !== 2 /* TRANSFER_STATUS_SENDER_KEY_TWEAKED */ && transfer.status !== 3 /* TRANSFER_STATUS_RECEIVER_KEY_TWEAKED */ && transfer.status !== 4 /* TRANSFER_STATUS_RECEIVER_REFUND_SIGNED */ && transfer.status !== 10 /* TRANSFER_STATUS_RECEIVER_KEY_TWEAK_APPLIED */ && transfer.status !== 9 /* TRANSFER_STATUS_RECEIVER_KEY_TWEAK_LOCKED */) {
11745
13384
  continue;
11746
13385
  }
11747
13386
  promises.push(
@@ -12029,6 +13668,8 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
12029
13668
  await this.transferService.deliverTransferPackage(
12030
13669
  swapResponse.transfer,
12031
13670
  leavesToSend,
13671
+ /* @__PURE__ */ new Map(),
13672
+ /* @__PURE__ */ new Map(),
12032
13673
  /* @__PURE__ */ new Map()
12033
13674
  );
12034
13675
  const sspResponse = await sspClient.requestLightningSend({
@@ -12308,11 +13949,12 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
12308
13949
  * Gets a transfer that has been sent by the SSP to the wallet.
12309
13950
  *
12310
13951
  * @param {string} id - The ID of the transfer
12311
- * @returns {Promise<GraphQLTransferObj | null>} The transfer
13952
+ * @returns {Promise<TransferWithUserRequest | undefined>} The transfer
12312
13953
  */
12313
13954
  async getTransferFromSsp(id) {
12314
13955
  const sspClient = this.getSspClient();
12315
- return await sspClient.getTransfer(id);
13956
+ const transfers = await sspClient.getTransfers([id]);
13957
+ return transfers?.[0];
12316
13958
  }
12317
13959
  /**
12318
13960
  * Gets a transfer, that the wallet is a participant of, in the Spark network.
@@ -12791,87 +14433,64 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
12791
14433
  },
12792
14434
  includeParents: true
12793
14435
  });
12794
- const node = response.nodes[nodeId];
12795
- if (!node) {
14436
+ let leaf = response.nodes[nodeId];
14437
+ if (!leaf) {
12796
14438
  throw new ValidationError("Node not found", {
12797
14439
  field: "nodeId",
12798
14440
  value: nodeId
12799
14441
  });
12800
14442
  }
12801
- if (!node.parentNodeId) {
12802
- throw new ValidationError("Node has no parent", {
12803
- field: "parentNodeId",
12804
- value: node.parentNodeId
12805
- });
12806
- }
12807
- const parentNode = response.nodes[node.parentNodeId];
12808
- if (!parentNode) {
12809
- throw new ValidationError("Parent node not found", {
12810
- field: "parentNodeId",
12811
- value: node.parentNodeId
12812
- });
12813
- }
12814
- const result = await this.transferService.refreshTimelockNodes(
12815
- [node],
12816
- parentNode
12817
- );
12818
- const leafIndex = this.leaves.findIndex((leaf) => leaf.id === node.id);
12819
- if (leafIndex !== -1 && result.nodes.length > 0) {
12820
- const newNode = result.nodes[0];
12821
- if (newNode) {
12822
- this.leaves[leafIndex] = newNode;
14443
+ let parentNode;
14444
+ let hasParentNode = false;
14445
+ if (!leaf.parentNodeId) {
14446
+ } else {
14447
+ hasParentNode = true;
14448
+ parentNode = response.nodes[leaf.parentNodeId];
14449
+ if (!parentNode) {
14450
+ throw new ValidationError("Parent node not found", {
14451
+ field: "parentNodeId",
14452
+ value: leaf.parentNodeId
14453
+ });
12823
14454
  }
12824
14455
  }
12825
- } catch (error) {
12826
- throw new NetworkError(
12827
- "Failed to refresh timelock",
12828
- {
12829
- method: "refresh_timelock"
12830
- },
12831
- error
12832
- );
12833
- }
12834
- }
12835
- /**
12836
- * Refresh the timelock of a specific node's refund transaction only.
12837
- *
12838
- * @param {string} nodeId - The ID of the node whose refund transaction to refresh
12839
- * @returns {Promise<void>} Promise that resolves when the refund timelock is refreshed
12840
- */
12841
- async testOnly_expireTimelockRefundTx(nodeId) {
12842
- const sparkClient = await this.connectionManager.createSparkClient(
12843
- this.config.getCoordinatorAddress()
12844
- );
12845
- try {
12846
- const response = await sparkClient.query_nodes({
12847
- source: {
12848
- $case: "nodeIds",
12849
- nodeIds: {
12850
- nodeIds: [nodeId]
14456
+ const nodeTx = getTxFromRawTxBytes(leaf.nodeTx);
14457
+ const refundTx = getTxFromRawTxBytes(leaf.refundTx);
14458
+ if (hasParentNode) {
14459
+ const nodeTimelock = getCurrentTimelock(nodeTx.getInput(0).sequence);
14460
+ if (nodeTimelock > 100) {
14461
+ const expiredNodeTxLeaf = await this.transferService.testonly_expireTimeLockNodeTx(
14462
+ leaf,
14463
+ parentNode
14464
+ );
14465
+ if (!expiredNodeTxLeaf.nodes[0]) {
14466
+ throw new ValidationError("No expired node tx leaf", {
14467
+ field: "expiredNodeTxLeaf",
14468
+ value: expiredNodeTxLeaf
14469
+ });
12851
14470
  }
12852
- },
12853
- includeParents: false
12854
- });
12855
- const node = response.nodes[nodeId];
12856
- if (!node) {
12857
- throw new ValidationError("Node not found", {
12858
- field: "nodeId",
12859
- value: nodeId
12860
- });
14471
+ leaf = expiredNodeTxLeaf.nodes[0];
14472
+ }
12861
14473
  }
12862
- const result = await this.transferService.refreshTimelockRefundTx(node);
12863
- const leafIndex = this.leaves.findIndex((leaf) => leaf.id === node.id);
12864
- if (leafIndex !== -1 && result.nodes.length > 0) {
12865
- const newNode = result.nodes[0];
12866
- if (newNode) {
12867
- this.leaves[leafIndex] = newNode;
14474
+ const refundTimelock = getCurrentTimelock(refundTx.getInput(0).sequence);
14475
+ if (refundTimelock > 100) {
14476
+ const expiredTxLeaf = await this.transferService.testonly_expireTimeLockRefundtx(leaf);
14477
+ if (!expiredTxLeaf.nodes[0]) {
14478
+ throw new ValidationError("No expired tx leaf", {
14479
+ field: "expiredTxLeaf",
14480
+ value: expiredTxLeaf
14481
+ });
12868
14482
  }
14483
+ leaf = expiredTxLeaf.nodes[0];
14484
+ }
14485
+ const leafIndex = this.leaves.findIndex((leaf2) => leaf2.id === leaf2.id);
14486
+ if (leafIndex !== -1) {
14487
+ this.leaves[leafIndex] = leaf;
12869
14488
  }
12870
14489
  } catch (error) {
12871
14490
  throw new NetworkError(
12872
- "Failed to refresh refund timelock",
14491
+ "Failed to refresh timelock",
12873
14492
  {
12874
- method: "refresh_timelock_refund_tx"
14493
+ method: "refresh_timelock"
12875
14494
  },
12876
14495
  error
12877
14496
  );
@@ -12933,16 +14552,17 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
12933
14552
 
12934
14553
  // src/utils/mempool.ts
12935
14554
  async function getLatestDepositTxId(address2) {
14555
+ const { fetch: fetch2, Headers: Headers2 } = getFetch();
12936
14556
  const network = getNetworkFromAddress(address2);
12937
14557
  const baseUrl = network === BitcoinNetwork_default.REGTEST ? getElectrsUrl("REGTEST") : getElectrsUrl("MAINNET");
12938
- const headers = {};
14558
+ const headers = new Headers2();
12939
14559
  if (network === BitcoinNetwork_default.REGTEST) {
12940
14560
  const auth = btoa(
12941
14561
  `${ELECTRS_CREDENTIALS.username}:${ELECTRS_CREDENTIALS.password}`
12942
14562
  );
12943
- headers["Authorization"] = `Basic ${auth}`;
14563
+ headers.set("Authorization", `Basic ${auth}`);
12944
14564
  }
12945
- const response = await fetch(`${baseUrl}/address/${address2}/txs`, {
14565
+ const response = await fetch2(`${baseUrl}/address/${address2}/txs`, {
12946
14566
  headers
12947
14567
  });
12948
14568
  const addressTxs = await response.json();
@@ -12959,14 +14579,15 @@ async function getLatestDepositTxId(address2) {
12959
14579
  return null;
12960
14580
  }
12961
14581
  async function isTxBroadcast(txid, baseUrl, network) {
12962
- const headers = {};
14582
+ const { fetch: fetch2, Headers: Headers2 } = getFetch();
14583
+ const headers = new Headers2();
12963
14584
  if (network === 3 /* REGTEST */) {
12964
14585
  const auth = btoa(
12965
14586
  `${ELECTRS_CREDENTIALS.username}:${ELECTRS_CREDENTIALS.password}`
12966
14587
  );
12967
- headers["Authorization"] = `Basic ${auth}`;
14588
+ headers.set("Authorization", `Basic ${auth}`);
12968
14589
  }
12969
- const response = await fetch(`${baseUrl}/tx/${txid}`, {
14590
+ const response = await fetch2(`${baseUrl}/tx/${txid}`, {
12970
14591
  headers
12971
14592
  });
12972
14593
  const tx = await response.json();
@@ -12980,7 +14601,7 @@ async function isTxBroadcast(txid, baseUrl, network) {
12980
14601
  import { bytesToHex as bytesToHex12, hexToBytes as hexToBytes13 } from "@noble/curves/abstract/utils";
12981
14602
  import { ripemd160 } from "@noble/hashes/legacy";
12982
14603
  import { sha256 as sha25612 } from "@noble/hashes/sha2";
12983
- import * as btc5 from "@scure/btc-signer";
14604
+ import * as btc4 from "@scure/btc-signer";
12984
14605
  function isEphemeralAnchorOutput(script, amount) {
12985
14606
  return Boolean(
12986
14607
  amount === 0n && script && // Pattern 1: Bare OP_TRUE (single byte 0x51)
@@ -13195,7 +14816,7 @@ async function constructUnilateralExitFeeBumpPackages(nodeHexStrings, utxos, fee
13195
14816
  usedUtxos,
13196
14817
  correctedParentTx
13197
14818
  } = constructFeeBumpTx(nodeTxHex, availableUtxos, feeRate, void 0);
13198
- const feeBumpTx = btc5.Transaction.fromPSBT(hexToBytes13(nodeFeeBumpPsbt));
14819
+ const feeBumpTx = btc4.Transaction.fromPSBT(hexToBytes13(nodeFeeBumpPsbt));
13199
14820
  var feeBumpOut = feeBumpTx.outputsLength === 1 ? feeBumpTx.getOutput(0) : null;
13200
14821
  var feeBumpOutPubKey = null;
13201
14822
  for (const usedUtxo of usedUtxos) {
@@ -13249,7 +14870,7 @@ async function constructUnilateralExitFeeBumpPackages(nodeHexStrings, utxos, fee
13249
14870
  feeRate,
13250
14871
  void 0
13251
14872
  );
13252
- const feeBumpTx2 = btc5.Transaction.fromPSBT(
14873
+ const feeBumpTx2 = btc4.Transaction.fromPSBT(
13253
14874
  hexToBytes13(refundFeeBump.feeBumpPsbt)
13254
14875
  );
13255
14876
  var feeBumpOut = feeBumpTx2.outputsLength === 1 ? feeBumpTx2.getOutput(0) : null;
@@ -13353,7 +14974,7 @@ function constructFeeBumpTx(txHex, utxos, feeRate, previousFeeBumpTx) {
13353
14974
  if (utxos.length === 0) {
13354
14975
  throw new Error("No UTXOs available for fee bump");
13355
14976
  }
13356
- const builder = new btc5.Transaction({
14977
+ const builder = new btc4.Transaction({
13357
14978
  version: 3,
13358
14979
  allowUnknown: true,
13359
14980
  allowLegacyWitnessUtxo: true
@@ -13445,19 +15066,19 @@ function constructFeeBumpTx(txHex, utxos, feeRate, previousFeeBumpTx) {
13445
15066
  }
13446
15067
 
13447
15068
  // src/utils/xchain-address.ts
13448
- import * as btc6 from "@scure/btc-signer";
15069
+ import * as btc5 from "@scure/btc-signer";
13449
15070
  var networkByType = {
13450
- MAINNET: btc6.NETWORK,
13451
- TESTNET: btc6.TEST_NETWORK,
15071
+ MAINNET: btc5.NETWORK,
15072
+ TESTNET: btc5.TEST_NETWORK,
13452
15073
  REGTEST: {
13453
- ...btc6.TEST_NETWORK,
15074
+ ...btc5.TEST_NETWORK,
13454
15075
  bech32: "bcrt"
13455
15076
  }
13456
15077
  };
13457
15078
  function getSparkAddressFromTaproot(taprootAddress) {
13458
15079
  for (const networkType of ["MAINNET", "TESTNET", "REGTEST"]) {
13459
15080
  try {
13460
- const result = btc6.Address(networkByType[networkType]).decode(taprootAddress);
15081
+ const result = btc5.Address(networkByType[networkType]).decode(taprootAddress);
13461
15082
  if (result.type === "tr") {
13462
15083
  const outputPublicKey = result.pubkey;
13463
15084
  return encodeSparkAddress({
@@ -13478,10 +15099,15 @@ function getSparkAddressFromTaproot(taprootAddress) {
13478
15099
  var utils_exports = {};
13479
15100
  __export(utils_exports, {
13480
15101
  DEFAULT_FEE_SATS: () => DEFAULT_FEE_SATS,
15102
+ DIRECT_TIMELOCK_OFFSET: () => DIRECT_TIMELOCK_OFFSET,
15103
+ INITIAL_DIRECT_SEQUENCE: () => INITIAL_DIRECT_SEQUENCE,
15104
+ INITIAL_SEQUENCE: () => INITIAL_SEQUENCE,
13481
15105
  LRC_WALLET_NETWORK: () => LRC_WALLET_NETWORK,
13482
15106
  LRC_WALLET_NETWORK_TYPE: () => LRC_WALLET_NETWORK_TYPE,
13483
15107
  Network: () => Network2,
13484
15108
  NetworkToProto: () => NetworkToProto,
15109
+ TEST_UNILATERAL_DIRECT_SEQUENCE: () => TEST_UNILATERAL_DIRECT_SEQUENCE,
15110
+ TEST_UNILATERAL_SEQUENCE: () => TEST_UNILATERAL_SEQUENCE,
13485
15111
  addPrivateKeys: () => addPrivateKeys,
13486
15112
  addPublicKeys: () => addPublicKeys,
13487
15113
  applyAdaptorToSignature: () => applyAdaptorToSignature,
@@ -13495,13 +15121,21 @@ __export(utils_exports, {
13495
15121
  constructFeeBumpTx: () => constructFeeBumpTx,
13496
15122
  constructUnilateralExitFeeBumpPackages: () => constructUnilateralExitFeeBumpPackages,
13497
15123
  constructUnilateralExitTxs: () => constructUnilateralExitTxs,
15124
+ createConnectorRefundTransactions: () => createConnectorRefundTransactions,
15125
+ createLeafNodeTx: () => createLeafNodeTx,
15126
+ createNodeTx: () => createNodeTx,
15127
+ createNodeTxs: () => createNodeTxs,
13498
15128
  createRefundTx: () => createRefundTx,
15129
+ createRefundTxs: () => createRefundTxs,
15130
+ createRootTx: () => createRootTx,
13499
15131
  createSigningCommitment: () => createSigningCommitment,
13500
15132
  createSigningNonce: () => createSigningNonce,
15133
+ createSplitTx: () => createSplitTx,
13501
15134
  decodeBech32mTokenIdentifier: () => decodeBech32mTokenIdentifier,
13502
15135
  decodeBytesToSigningCommitment: () => decodeBytesToSigningCommitment,
13503
15136
  decodeBytesToSigningNonce: () => decodeBytesToSigningNonce,
13504
15137
  decodeSparkAddress: () => decodeSparkAddress,
15138
+ doesLeafNeedRefresh: () => doesLeafNeedRefresh,
13505
15139
  encodeBech32mTokenIdentifier: () => encodeBech32mTokenIdentifier,
13506
15140
  encodeSigningCommitmentToBytes: () => encodeSigningCommitmentToBytes,
13507
15141
  encodeSigningNonceToBytes: () => encodeSigningNonceToBytes,
@@ -13530,6 +15164,7 @@ __export(utils_exports, {
13530
15164
  getSparkAddressFromTaproot: () => getSparkAddressFromTaproot,
13531
15165
  getTransactionSequence: () => getTransactionSequence,
13532
15166
  getTransferPackageSigningPayload: () => getTransferPackageSigningPayload,
15167
+ getTxEstimatedVbytesSizeByNumberOfInputsOutputs: () => getTxEstimatedVbytesSizeByNumberOfInputsOutputs,
13533
15168
  getTxFromRawTxBytes: () => getTxFromRawTxBytes,
13534
15169
  getTxFromRawTxHex: () => getTxFromRawTxHex,
13535
15170
  getTxId: () => getTxId,
@@ -13617,12 +15252,26 @@ export {
13617
15252
  getSigHashFromTx,
13618
15253
  getTxId,
13619
15254
  getTxIdNoReverse,
15255
+ getTxEstimatedVbytesSizeByNumberOfInputsOutputs,
15256
+ DIRECT_TIMELOCK_OFFSET,
15257
+ INITIAL_SEQUENCE,
15258
+ INITIAL_DIRECT_SEQUENCE,
15259
+ TEST_UNILATERAL_SEQUENCE,
15260
+ TEST_UNILATERAL_DIRECT_SEQUENCE,
13620
15261
  DEFAULT_FEE_SATS,
13621
15262
  maybeApplyFee,
15263
+ createRootTx,
15264
+ createSplitTx,
15265
+ createNodeTx,
15266
+ createNodeTxs,
15267
+ createLeafNodeTx,
13622
15268
  createRefundTx,
15269
+ createRefundTxs,
15270
+ createConnectorRefundTransactions,
13623
15271
  getCurrentTimelock,
13624
15272
  getTransactionSequence,
13625
15273
  checkIfValidSequence,
15274
+ doesLeafNeedRefresh,
13626
15275
  getNextTransactionSequence,
13627
15276
  getEphemeralAnchorOutput,
13628
15277
  getTransferPackageSigningPayload,