@buildonspark/spark-sdk 0.1.43 → 0.1.44

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 (112) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/{RequestLightningSendInput-Na1mHdWg.d.cts → RequestLightningSendInput-BxbCtwpV.d.cts} +43 -4
  3. package/dist/{RequestLightningSendInput-D7fZdT4A.d.ts → RequestLightningSendInput-RGel43ks.d.ts} +43 -4
  4. package/dist/address/index.d.cts +2 -2
  5. package/dist/address/index.d.ts +2 -2
  6. package/dist/address/index.js +2 -2
  7. package/dist/{chunk-M6A4KFIG.js → chunk-4Q2ZDYYU.js} +332 -221
  8. package/dist/{chunk-WWOTVNPP.js → chunk-A2ZLMH6I.js} +323 -142
  9. package/dist/{chunk-VA7MV4MZ.js → chunk-B3AMIGJG.js} +1 -1
  10. package/dist/{chunk-DQYKQJRZ.js → chunk-CIZNCBKE.js} +29 -9
  11. package/dist/{chunk-BUTZWYBW.js → chunk-DAXGVPVM.js} +2 -2
  12. package/dist/{chunk-DOA6QXYQ.js → chunk-EKFD62HN.js} +2 -1
  13. package/dist/{chunk-TIUBYNN5.js → chunk-HTMXTJRK.js} +1 -1
  14. package/dist/{chunk-TOSP3INR.js → chunk-I54FARY2.js} +4 -2
  15. package/dist/{chunk-MIVX3GHD.js → chunk-K4BJARWM.js} +1 -1
  16. package/dist/{chunk-O4RYNJNB.js → chunk-KEKGSH7B.js} +1 -1
  17. package/dist/{chunk-GYQR4B4P.js → chunk-NBCNYDWJ.js} +2 -2
  18. package/dist/{chunk-ABZA6R5S.js → chunk-SQKXGAIR.js} +1 -1
  19. package/dist/{chunk-VFJQNBFX.js → chunk-UBT6EDVJ.js} +5 -2
  20. package/dist/{chunk-HRQRRDSS.js → chunk-WPTRVD2V.js} +3 -3
  21. package/dist/{chunk-IRW5TWMH.js → chunk-XX4RRWOX.js} +5 -5
  22. package/dist/graphql/objects/index.d.cts +5 -43
  23. package/dist/graphql/objects/index.d.ts +5 -43
  24. package/dist/graphql/objects/index.js +1 -1
  25. package/dist/{index-BJOc8Ur-.d.cts → index-CZmDdSts.d.cts} +24 -10
  26. package/dist/{index-7RYRH5wc.d.ts → index-ClIRO_3y.d.ts} +24 -10
  27. package/dist/index.cjs +827 -414
  28. package/dist/index.d.cts +6 -6
  29. package/dist/index.d.ts +6 -6
  30. package/dist/index.js +15 -15
  31. package/dist/index.node.cjs +830 -417
  32. package/dist/index.node.d.cts +7 -7
  33. package/dist/index.node.d.ts +7 -7
  34. package/dist/index.node.js +18 -18
  35. package/dist/native/index.cjs +827 -414
  36. package/dist/native/index.d.cts +88 -23
  37. package/dist/native/index.d.ts +88 -23
  38. package/dist/native/index.js +819 -407
  39. package/dist/{network-D5lKssVl.d.cts → network-CfxLnaot.d.cts} +1 -1
  40. package/dist/{network-xkBSpaTn.d.ts → network-CroCOQ0B.d.ts} +1 -1
  41. package/dist/proto/lrc20.d.cts +1 -1
  42. package/dist/proto/lrc20.d.ts +1 -1
  43. package/dist/proto/lrc20.js +2 -2
  44. package/dist/proto/spark.cjs +332 -221
  45. package/dist/proto/spark.d.cts +1 -1
  46. package/dist/proto/spark.d.ts +1 -1
  47. package/dist/proto/spark.js +3 -5
  48. package/dist/proto/spark_token.cjs +149 -9
  49. package/dist/proto/spark_token.d.cts +1 -1
  50. package/dist/proto/spark_token.d.ts +1 -1
  51. package/dist/proto/spark_token.js +2 -2
  52. package/dist/{sdk-types-B-q9py_P.d.cts → sdk-types-BeCBoozO.d.cts} +1 -1
  53. package/dist/{sdk-types-BPoPgzda.d.ts → sdk-types-CTbTdDbE.d.ts} +1 -1
  54. package/dist/services/config.cjs +7 -3
  55. package/dist/services/config.d.cts +4 -4
  56. package/dist/services/config.d.ts +4 -4
  57. package/dist/services/config.js +5 -5
  58. package/dist/services/connection.cjs +334 -218
  59. package/dist/services/connection.d.cts +4 -4
  60. package/dist/services/connection.d.ts +4 -4
  61. package/dist/services/connection.js +4 -4
  62. package/dist/services/index.cjs +364 -227
  63. package/dist/services/index.d.cts +4 -4
  64. package/dist/services/index.d.ts +4 -4
  65. package/dist/services/index.js +9 -9
  66. package/dist/services/lrc-connection.cjs +5 -2
  67. package/dist/services/lrc-connection.d.cts +4 -4
  68. package/dist/services/lrc-connection.d.ts +4 -4
  69. package/dist/services/lrc-connection.js +4 -4
  70. package/dist/services/token-transactions.cjs +28 -8
  71. package/dist/services/token-transactions.d.cts +4 -4
  72. package/dist/services/token-transactions.d.ts +4 -4
  73. package/dist/services/token-transactions.js +3 -3
  74. package/dist/services/wallet-config.cjs +2 -1
  75. package/dist/services/wallet-config.d.cts +4 -4
  76. package/dist/services/wallet-config.d.ts +4 -4
  77. package/dist/services/wallet-config.js +1 -1
  78. package/dist/signer/signer.cjs +5 -2
  79. package/dist/signer/signer.d.cts +2 -2
  80. package/dist/signer/signer.d.ts +2 -2
  81. package/dist/signer/signer.js +2 -2
  82. package/dist/{signer-wqesWifN.d.ts → signer-D7vfYik9.d.ts} +1 -1
  83. package/dist/{signer-IO3oMRNj.d.cts → signer-DaY8c60s.d.cts} +1 -1
  84. package/dist/{spark-CDm4gqS6.d.ts → spark-C4ZrsgjC.d.cts} +47 -29
  85. package/dist/{spark-CDm4gqS6.d.cts → spark-C4ZrsgjC.d.ts} +47 -29
  86. package/dist/types/index.cjs +331 -219
  87. package/dist/types/index.d.cts +5 -5
  88. package/dist/types/index.d.ts +5 -5
  89. package/dist/types/index.js +3 -3
  90. package/dist/utils/index.cjs +298 -28
  91. package/dist/utils/index.d.cts +5 -5
  92. package/dist/utils/index.d.ts +5 -5
  93. package/dist/utils/index.js +6 -6
  94. package/package.json +1 -1
  95. package/src/constants.ts +5 -1
  96. package/src/graphql/client.ts +28 -0
  97. package/src/graphql/mutations/RequestCoopExit.ts +6 -0
  98. package/src/graphql/mutations/RequestSwapLeaves.ts +2 -0
  99. package/src/graphql/queries/GetCoopExitFeeQuote.ts +20 -0
  100. package/src/proto/spark.ts +454 -322
  101. package/src/services/token-transactions.ts +12 -0
  102. package/src/services/transfer.ts +0 -3
  103. package/src/services/wallet-config.ts +1 -0
  104. package/src/spark-wallet/spark-wallet.node.ts +3 -3
  105. package/src/spark-wallet/spark-wallet.ts +301 -125
  106. package/src/tests/integration/deposit.test.ts +16 -0
  107. package/src/tests/integration/ssp/coop-exit.test.ts +85 -21
  108. package/src/tests/integration/ssp/swap.test.ts +47 -0
  109. package/src/tests/tokens.test.ts +3 -2
  110. package/src/utils/token-hashing.ts +28 -10
  111. package/src/utils/transaction.ts +2 -2
  112. package/src/logger.ts +0 -3
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  ConnectionManager
3
- } from "./chunk-HRQRRDSS.js";
3
+ } from "./chunk-WPTRVD2V.js";
4
4
  import {
5
5
  Lrc20ConnectionManager
6
- } from "./chunk-GYQR4B4P.js";
6
+ } from "./chunk-NBCNYDWJ.js";
7
7
  import {
8
8
  TokenTransactionService
9
- } from "./chunk-DQYKQJRZ.js";
9
+ } from "./chunk-CIZNCBKE.js";
10
10
  import {
11
11
  DEFAULT_FEE_SATS,
12
12
  computeTaprootKeyNoScript,
@@ -23,16 +23,17 @@ import {
23
23
  getTxFromRawTxHex,
24
24
  getTxId,
25
25
  proofOfPossessionMessageHashForDepositAddress
26
- } from "./chunk-IRW5TWMH.js";
26
+ } from "./chunk-XX4RRWOX.js";
27
27
  import {
28
28
  calculateAvailableTokenAmount
29
29
  } from "./chunk-6AFUC5M2.js";
30
30
  import {
31
31
  mapTransferToWalletTransfer,
32
32
  mapTreeNodeToWalletLeaf
33
- } from "./chunk-MIVX3GHD.js";
33
+ } from "./chunk-K4BJARWM.js";
34
34
  import {
35
35
  ClaimStaticDepositRequestType_default,
36
+ CoopExitFeeQuoteFromJson,
36
37
  CurrencyAmountFromJson,
37
38
  ExitSpeed_default,
38
39
  FRAGMENT,
@@ -40,20 +41,21 @@ import {
40
41
  FRAGMENT3,
41
42
  FRAGMENT4,
42
43
  FRAGMENT5,
44
+ FRAGMENT6,
43
45
  LeavesSwapRequestFromJson,
44
46
  LightningReceiveRequestFromJson,
45
47
  LightningSendRequestFromJson,
46
48
  SparkCoopExitRequestStatus_default,
47
49
  TransferFromJson
48
- } from "./chunk-TOSP3INR.js";
50
+ } from "./chunk-I54FARY2.js";
49
51
  import {
50
52
  decodeSparkAddress,
51
53
  encodeSparkAddress,
52
54
  isValidPublicKey
53
- } from "./chunk-O4RYNJNB.js";
55
+ } from "./chunk-KEKGSH7B.js";
54
56
  import {
55
57
  WalletConfigService
56
- } from "./chunk-BUTZWYBW.js";
58
+ } from "./chunk-DAXGVPVM.js";
57
59
  import {
58
60
  LRC_WALLET_NETWORK,
59
61
  LRC_WALLET_NETWORK_TYPE,
@@ -63,17 +65,17 @@ import {
63
65
  } from "./chunk-Z5HIAYFT.js";
64
66
  import {
65
67
  ELECTRS_CREDENTIALS
66
- } from "./chunk-DOA6QXYQ.js";
68
+ } from "./chunk-EKFD62HN.js";
67
69
  import {
68
70
  BitcoinNetwork_default
69
71
  } from "./chunk-HMLOC6TE.js";
70
72
  import {
71
73
  SendLeafKeyTweaks,
72
74
  networkToJSON
73
- } from "./chunk-M6A4KFIG.js";
75
+ } from "./chunk-4Q2ZDYYU.js";
74
76
  import {
75
77
  isReactNative
76
- } from "./chunk-VFJQNBFX.js";
78
+ } from "./chunk-UBT6EDVJ.js";
77
79
  import {
78
80
  addPublicKeys,
79
81
  applyAdaptorToSignature,
@@ -126,7 +128,7 @@ var ClaimStaticDepositOutputFromJson = (obj) => {
126
128
  transferId: obj["claim_static_deposit_output_transfer_id"]
127
129
  };
128
130
  };
129
- var FRAGMENT6 = `
131
+ var FRAGMENT7 = `
130
132
  fragment ClaimStaticDepositOutputFragment on ClaimStaticDepositOutput {
131
133
  __typename
132
134
  claim_static_deposit_output_transfer_id: transfer_id
@@ -159,7 +161,7 @@ var ClaimStaticDeposit = `
159
161
  }
160
162
  }
161
163
 
162
- ${FRAGMENT6}
164
+ ${FRAGMENT7}
163
165
  `;
164
166
 
165
167
  // src/graphql/objects/CoopExitRequest.ts
@@ -185,7 +187,7 @@ var CoopExitRequestFromJson = (obj) => {
185
187
  transfer: !!obj["coop_exit_request_transfer"] ? TransferFromJson(obj["coop_exit_request_transfer"]) : void 0
186
188
  };
187
189
  };
188
- var FRAGMENT7 = `
190
+ var FRAGMENT8 = `
189
191
  fragment CoopExitRequestFragment on CoopExitRequest {
190
192
  __typename
191
193
  coop_exit_request_id: id
@@ -250,7 +252,7 @@ var CompleteCoopExit = `
250
252
  }
251
253
  }
252
254
 
253
- ${FRAGMENT7}
255
+ ${FRAGMENT8}
254
256
  `;
255
257
 
256
258
  // src/graphql/mutations/CompleteLeavesSwap.ts
@@ -276,7 +278,7 @@ var GetChallengeOutputFromJson = (obj) => {
276
278
  protectedChallenge: obj["get_challenge_output_protected_challenge"]
277
279
  };
278
280
  };
279
- var FRAGMENT8 = `
281
+ var FRAGMENT9 = `
280
282
  fragment GetChallengeOutputFragment on GetChallengeOutput {
281
283
  __typename
282
284
  get_challenge_output_protected_challenge: protected_challenge
@@ -294,7 +296,7 @@ var GetChallenge = `
294
296
  }
295
297
  }
296
298
 
297
- ${FRAGMENT8}
299
+ ${FRAGMENT9}
298
300
  `;
299
301
 
300
302
  // src/graphql/mutations/RequestCoopExit.ts
@@ -304,6 +306,9 @@ var RequestCoopExit = `
304
306
  $withdrawal_address: String!
305
307
  $idempotency_key: String!
306
308
  $exit_speed: ExitSpeed!
309
+ $withdraw_all: Boolean
310
+ $fee_leaf_external_ids: [UUID!]
311
+ $fee_quote_id: ID
307
312
  ) {
308
313
  request_coop_exit(
309
314
  input: {
@@ -311,6 +316,9 @@ var RequestCoopExit = `
311
316
  withdrawal_address: $withdrawal_address
312
317
  idempotency_key: $idempotency_key
313
318
  exit_speed: $exit_speed
319
+ withdraw_all: $withdraw_all
320
+ fee_leaf_external_ids: $fee_leaf_external_ids
321
+ fee_quote_id: $fee_quote_id
314
322
  }
315
323
  ) {
316
324
  request {
@@ -318,7 +326,7 @@ var RequestCoopExit = `
318
326
  }
319
327
  }
320
328
  }
321
- ${FRAGMENT7}
329
+ ${FRAGMENT8}
322
330
  `;
323
331
 
324
332
  // src/graphql/mutations/RequestLightningReceive.ts
@@ -382,6 +390,7 @@ var RequestSwapLeaves = `
382
390
  $fee_sats: Long!
383
391
  $user_leaves: [UserLeafInput!]!
384
392
  $idempotency_key: String!
393
+ $target_amount_sats_list: [Long!]
385
394
  ) {
386
395
  request_leaves_swap(input: {
387
396
  adaptor_pubkey: $adaptor_pubkey
@@ -390,6 +399,7 @@ var RequestSwapLeaves = `
390
399
  fee_sats: $fee_sats
391
400
  user_leaves: $user_leaves
392
401
  idempotency_key: $idempotency_key
402
+ target_amount_sats_list: $target_amount_sats_list
393
403
  }) {
394
404
  request {
395
405
  ...LeavesSwapRequestFragment
@@ -406,7 +416,7 @@ var VerifyChallengeOutputFromJson = (obj) => {
406
416
  sessionToken: obj["verify_challenge_output_session_token"]
407
417
  };
408
418
  };
409
- var FRAGMENT9 = `
419
+ var FRAGMENT10 = `
410
420
  fragment VerifyChallengeOutputFragment on VerifyChallengeOutput {
411
421
  __typename
412
422
  verify_challenge_output_valid_until: valid_until
@@ -431,7 +441,7 @@ var VerifyChallenge = `
431
441
  }
432
442
  }
433
443
 
434
- ${FRAGMENT9}
444
+ ${FRAGMENT10}
435
445
  `;
436
446
 
437
447
  // src/graphql/objects/CoopExitFeeEstimate.ts
@@ -450,7 +460,7 @@ var CoopExitFeeEstimatesOutputFromJson = (obj) => {
450
460
  speedSlow: !!obj["coop_exit_fee_estimates_output_speed_slow"] ? CoopExitFeeEstimateFromJson(obj["coop_exit_fee_estimates_output_speed_slow"]) : void 0
451
461
  };
452
462
  };
453
- var FRAGMENT10 = `
463
+ var FRAGMENT11 = `
454
464
  fragment CoopExitFeeEstimatesOutputFragment on CoopExitFeeEstimatesOutput {
455
465
  __typename
456
466
  coop_exit_fee_estimates_output_speed_fast: speed_fast {
@@ -518,7 +528,7 @@ var LeavesSwapFeeEstimateOutputFromJson = (obj) => {
518
528
  feeEstimate: CurrencyAmountFromJson(obj["leaves_swap_fee_estimate_output_fee_estimate"])
519
529
  };
520
530
  };
521
- var FRAGMENT11 = `
531
+ var FRAGMENT12 = `
522
532
  fragment LeavesSwapFeeEstimateOutputFragment on LeavesSwapFeeEstimateOutput {
523
533
  __typename
524
534
  leaves_swap_fee_estimate_output_fee_estimate: fee_estimate {
@@ -537,7 +547,7 @@ var LightningSendFeeEstimateOutputFromJson = (obj) => {
537
547
  feeEstimate: CurrencyAmountFromJson(obj["lightning_send_fee_estimate_output_fee_estimate"])
538
548
  };
539
549
  };
540
- var FRAGMENT12 = `
550
+ var FRAGMENT13 = `
541
551
  fragment LightningSendFeeEstimateOutputFragment on LightningSendFeeEstimateOutput {
542
552
  __typename
543
553
  lightning_send_fee_estimate_output_fee_estimate: fee_estimate {
@@ -560,7 +570,7 @@ var StaticDepositQuoteOutputFromJson = (obj) => {
560
570
  signature: obj["static_deposit_quote_output_signature"]
561
571
  };
562
572
  };
563
- var FRAGMENT13 = `
573
+ var FRAGMENT14 = `
564
574
  fragment StaticDepositQuoteOutputFragment on StaticDepositQuoteOutput {
565
575
  __typename
566
576
  static_deposit_quote_output_transaction_id: transaction_id
@@ -585,7 +595,7 @@ var CoopExitFeeEstimate2 = `
585
595
  ...CoopExitFeeEstimatesOutputFragment
586
596
  }
587
597
  }
588
- ${FRAGMENT10}
598
+ ${FRAGMENT11}
589
599
  `;
590
600
 
591
601
  // src/graphql/queries/GetClaimDepositQuote.ts
@@ -605,7 +615,27 @@ var GetClaimDepositQuote = `
605
615
  ...StaticDepositQuoteOutputFragment
606
616
  }
607
617
  }
608
- ${FRAGMENT13}
618
+ ${FRAGMENT14}
619
+ `;
620
+
621
+ // src/graphql/queries/GetCoopExitFeeQuote.ts
622
+ var GetCoopExitFeeQuote = `
623
+ query CoopExitFeeQuote(
624
+ $leaf_external_ids: [UUID!]!
625
+ $withdrawal_address: String!
626
+ ) {
627
+ coop_exit_fee_quote(
628
+ input: {
629
+ leaf_external_ids: $leaf_external_ids,
630
+ withdrawal_address: $withdrawal_address
631
+ }
632
+ ) {
633
+ quote {
634
+ ...CoopExitFeeQuoteFragment
635
+ }
636
+ }
637
+ }
638
+ ${FRAGMENT5}
609
639
  `;
610
640
 
611
641
  // src/graphql/queries/LeavesSwapFeeEstimate.ts
@@ -621,7 +651,7 @@ var LeavesSwapFeeEstimate = `
621
651
  ...LeavesSwapFeeEstimateOutputFragment
622
652
  }
623
653
  }
624
- ${FRAGMENT11}
654
+ ${FRAGMENT12}
625
655
  `;
626
656
 
627
657
  // src/graphql/queries/LightningSendFeeEstimate.ts
@@ -639,7 +669,7 @@ var LightningSendFeeEstimate = `
639
669
  ...LightningSendFeeEstimateOutputFragment
640
670
  }
641
671
  }
642
- ${FRAGMENT12}
672
+ ${FRAGMENT13}
643
673
  `;
644
674
 
645
675
  // src/graphql/queries/Transfer.ts
@@ -659,7 +689,7 @@ var UserRequest = `
659
689
  ...UserRequestFragment
660
690
  }
661
691
  }
662
- ${FRAGMENT5}
692
+ ${FRAGMENT6}
663
693
  `;
664
694
 
665
695
  // src/graphql/client.ts
@@ -781,7 +811,10 @@ var SspClient = class {
781
811
  leafExternalIds,
782
812
  withdrawalAddress,
783
813
  idempotencyKey,
784
- exitSpeed
814
+ exitSpeed,
815
+ feeLeafExternalIds,
816
+ feeQuoteId,
817
+ withdrawAll
785
818
  }) {
786
819
  return await this.executeRawQuery({
787
820
  queryPayload: RequestCoopExit,
@@ -789,7 +822,10 @@ var SspClient = class {
789
822
  leaf_external_ids: leafExternalIds,
790
823
  withdrawal_address: withdrawalAddress,
791
824
  idempotency_key: idempotencyKey,
792
- exit_speed: exitSpeed
825
+ exit_speed: exitSpeed,
826
+ fee_leaf_external_ids: feeLeafExternalIds,
827
+ fee_quote_id: feeQuoteId,
828
+ withdraw_all: withdrawAll
793
829
  },
794
830
  constructObject: (response) => {
795
831
  return CoopExitRequestFromJson(response.request_coop_exit.request);
@@ -851,7 +887,8 @@ var SspClient = class {
851
887
  targetAmountSats,
852
888
  feeSats,
853
889
  userLeaves,
854
- idempotencyKey
890
+ idempotencyKey,
891
+ targetAmountSatsList
855
892
  }) {
856
893
  const query = {
857
894
  queryPayload: RequestSwapLeaves,
@@ -861,7 +898,8 @@ var SspClient = class {
861
898
  target_amount_sats: targetAmountSats,
862
899
  fee_sats: feeSats,
863
900
  user_leaves: userLeaves,
864
- idempotency_key: idempotencyKey
901
+ idempotency_key: idempotencyKey,
902
+ target_amount_sats_list: targetAmountSatsList
865
903
  },
866
904
  constructObject: (response) => {
867
905
  if (!response.request_leaves_swap) {
@@ -1053,6 +1091,21 @@ var SspClient = class {
1053
1091
  new Date(verifyChallenge.validUntil)
1054
1092
  );
1055
1093
  }
1094
+ async getCoopExitFeeQuote({
1095
+ leafExternalIds,
1096
+ withdrawalAddress
1097
+ }) {
1098
+ return await this.executeRawQuery({
1099
+ queryPayload: GetCoopExitFeeQuote,
1100
+ variables: {
1101
+ leaf_external_ids: leafExternalIds,
1102
+ withdrawal_address: withdrawalAddress
1103
+ },
1104
+ constructObject: (response) => {
1105
+ return CoopExitFeeQuoteFromJson(response.coop_exit_fee_quote.quote);
1106
+ }
1107
+ });
1108
+ }
1056
1109
  };
1057
1110
  var SparkAuthProvider = class {
1058
1111
  sessionToken;
@@ -3250,7 +3303,7 @@ import { hexToBytes as hexToBytes4 } from "@noble/curves/abstract/utils";
3250
3303
  import { sha256 as sha2566 } from "@noble/hashes/sha2";
3251
3304
  import { Address as Address2, OutScript as OutScript2, Transaction as Transaction4 } from "@scure/btc-signer";
3252
3305
  var INITIAL_TIME_LOCK3 = 2e3;
3253
- function maybeApplyFee2(amount) {
3306
+ function maybeApplyFee(amount) {
3254
3307
  if (amount > BigInt(DEFAULT_FEE_SATS)) {
3255
3308
  return amount - BigInt(DEFAULT_FEE_SATS);
3256
3309
  }
@@ -3519,7 +3572,7 @@ var TreeCreationService = class {
3519
3572
  const refundPkScript = OutScript2.encode(refundAddress);
3520
3573
  refundTx.addOutput({
3521
3574
  script: refundPkScript,
3522
- amount: maybeApplyFee2(parentTxOut.amount)
3575
+ amount: maybeApplyFee(parentTxOut.amount)
3523
3576
  });
3524
3577
  refundTx.addOutput(getEphemeralAnchorOutput());
3525
3578
  const refundSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
@@ -4387,13 +4440,34 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
4387
4440
  ([_, node]) => node.status === "AVAILABLE" && !leavesToIgnore.has(node.id)
4388
4441
  ).map(([_, node]) => node);
4389
4442
  }
4390
- async selectLeaves(targetAmount) {
4391
- if (targetAmount <= 0) {
4443
+ async selectLeaves(targetAmounts) {
4444
+ if (targetAmounts.length === 0) {
4445
+ throw new ValidationError("Target amounts must be non-empty", {
4446
+ field: "targetAmounts",
4447
+ value: targetAmounts
4448
+ });
4449
+ }
4450
+ if (targetAmounts.some((amount) => amount <= 0)) {
4392
4451
  throw new ValidationError("Target amount must be positive", {
4393
- field: "targetAmount",
4394
- value: targetAmount
4452
+ field: "targetAmounts",
4453
+ value: targetAmounts
4395
4454
  });
4396
4455
  }
4456
+ const totalTargetAmount = targetAmounts.reduce(
4457
+ (acc, amount) => acc + amount,
4458
+ 0
4459
+ );
4460
+ const totalBalance = this.getInternalBalance();
4461
+ if (totalTargetAmount > totalBalance) {
4462
+ throw new ValidationError(
4463
+ "Total target amount exceeds available balance",
4464
+ {
4465
+ field: "targetAmounts",
4466
+ value: totalTargetAmount,
4467
+ expected: `less than or equal to ${totalBalance}`
4468
+ }
4469
+ );
4470
+ }
4397
4471
  const leaves = await this.getLeaves();
4398
4472
  if (leaves.length === 0) {
4399
4473
  throw new ValidationError("No owned leaves found", {
@@ -4401,33 +4475,49 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
4401
4475
  });
4402
4476
  }
4403
4477
  leaves.sort((a, b) => b.value - a.value);
4404
- let amount = 0;
4405
- let nodes = [];
4406
- for (const leaf of leaves) {
4407
- if (targetAmount - amount >= leaf.value) {
4408
- amount += leaf.value;
4409
- nodes.push(leaf);
4410
- }
4411
- }
4412
- if (amount !== targetAmount) {
4413
- await this.requestLeavesSwap({ targetAmount });
4414
- amount = 0;
4415
- nodes = [];
4416
- const newLeaves = await this.getLeaves();
4417
- newLeaves.sort((a, b) => b.value - a.value);
4418
- for (const leaf of newLeaves) {
4419
- if (targetAmount - amount >= leaf.value) {
4420
- amount += leaf.value;
4421
- nodes.push(leaf);
4478
+ const selectLeavesForTargets = (targetAmounts2, leaves2) => {
4479
+ const usedLeaves = /* @__PURE__ */ new Set();
4480
+ const results2 = /* @__PURE__ */ new Map();
4481
+ let totalAmount = 0;
4482
+ for (const targetAmount of targetAmounts2) {
4483
+ const nodes = [];
4484
+ let amount = 0;
4485
+ for (const leaf of leaves2) {
4486
+ if (usedLeaves.has(leaf.id)) {
4487
+ continue;
4488
+ }
4489
+ if (targetAmount - amount >= leaf.value) {
4490
+ amount += leaf.value;
4491
+ nodes.push(leaf);
4492
+ usedLeaves.add(leaf.id);
4493
+ }
4422
4494
  }
4495
+ totalAmount += amount;
4496
+ results2.set(targetAmount, nodes);
4423
4497
  }
4498
+ return {
4499
+ results: results2,
4500
+ foundSelections: totalAmount === totalTargetAmount
4501
+ };
4502
+ };
4503
+ let { results, foundSelections } = selectLeavesForTargets(
4504
+ targetAmounts,
4505
+ leaves
4506
+ );
4507
+ if (!foundSelections) {
4508
+ const newLeaves = await this.requestLeavesSwap({ targetAmounts });
4509
+ newLeaves.sort((a, b) => b.value - a.value);
4510
+ ({ results, foundSelections } = selectLeavesForTargets(
4511
+ targetAmounts,
4512
+ newLeaves
4513
+ ));
4424
4514
  }
4425
- if (nodes.reduce((acc, leaf) => acc + leaf.value, 0) !== targetAmount) {
4515
+ if (!foundSelections) {
4426
4516
  throw new Error(
4427
- `Failed to select leaves for target amount ${targetAmount}`
4517
+ `Failed to select leaves for target amount ${totalTargetAmount}`
4428
4518
  );
4429
4519
  }
4430
- return nodes;
4520
+ return results;
4431
4521
  }
4432
4522
  async selectLeavesForSwap(targetAmount) {
4433
4523
  if (targetAmount == 0) {
@@ -4660,27 +4750,44 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
4660
4750
  * @private
4661
4751
  */
4662
4752
  async requestLeavesSwap({
4663
- targetAmount,
4753
+ targetAmounts,
4664
4754
  leaves
4665
4755
  }) {
4666
- if (targetAmount && targetAmount <= 0) {
4667
- throw new Error("targetAmount must be positive");
4756
+ if (targetAmounts && targetAmounts.some((amount) => amount <= 0)) {
4757
+ throw new Error("specified targetAmount must be positive");
4668
4758
  }
4669
- if (targetAmount && !Number.isSafeInteger(targetAmount)) {
4759
+ if (targetAmounts && targetAmounts.some((amount) => !Number.isSafeInteger(amount))) {
4670
4760
  throw new ValidationError("targetAmount must be less than 2^53", {
4671
- field: "targetAmount",
4672
- value: targetAmount,
4761
+ field: "targetAmounts",
4762
+ value: targetAmounts,
4673
4763
  expected: "smaller or equal to " + Number.MAX_SAFE_INTEGER
4674
4764
  });
4675
4765
  }
4676
4766
  let leavesToSwap;
4677
- if (targetAmount && leaves && leaves.length > 0) {
4678
- if (targetAmount < leaves.reduce((acc, leaf) => acc + leaf.value, 0)) {
4767
+ const totalTargetAmount = targetAmounts?.reduce(
4768
+ (acc, amount) => acc + amount,
4769
+ 0
4770
+ );
4771
+ if (totalTargetAmount) {
4772
+ const totalBalance = this.getInternalBalance();
4773
+ if (totalTargetAmount > totalBalance) {
4774
+ throw new ValidationError(
4775
+ "Total target amount exceeds available balance",
4776
+ {
4777
+ field: "targetAmounts",
4778
+ value: totalTargetAmount,
4779
+ expected: `less than or equal to ${totalBalance}`
4780
+ }
4781
+ );
4782
+ }
4783
+ }
4784
+ if (totalTargetAmount && leaves && leaves.length > 0) {
4785
+ if (totalTargetAmount < leaves.reduce((acc, leaf) => acc + leaf.value, 0)) {
4679
4786
  throw new Error("targetAmount is less than the sum of leaves");
4680
4787
  }
4681
4788
  leavesToSwap = leaves;
4682
- } else if (targetAmount) {
4683
- leavesToSwap = await this.selectLeavesForSwap(targetAmount);
4789
+ } else if (totalTargetAmount) {
4790
+ leavesToSwap = await this.selectLeavesForSwap(totalTargetAmount);
4684
4791
  } else if (leaves && leaves.length > 0) {
4685
4792
  leavesToSwap = leaves;
4686
4793
  } else {
@@ -4690,15 +4797,15 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
4690
4797
  const batches = chunkArray(leavesToSwap, 100);
4691
4798
  const results = [];
4692
4799
  for (const batch of batches) {
4693
- const result = await this.processSwapBatch(batch, targetAmount);
4694
- results.push(...result.swapLeaves);
4800
+ const result = await this.processSwapBatch(batch, targetAmounts);
4801
+ results.push(...result);
4695
4802
  }
4696
4803
  return results;
4697
4804
  }
4698
4805
  /**
4699
4806
  * Processes a single batch of leaves for swapping.
4700
4807
  */
4701
- async processSwapBatch(leavesBatch, targetAmount) {
4808
+ async processSwapBatch(leavesBatch, targetAmounts) {
4702
4809
  const leafKeyTweaks = await Promise.all(
4703
4810
  leavesBatch.map(async (leaf) => ({
4704
4811
  leaf,
@@ -4762,8 +4869,9 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
4762
4869
  request = await sspClient.requestLeaveSwap({
4763
4870
  userLeaves,
4764
4871
  adaptorPubkey,
4765
- targetAmountSats: targetAmount || leavesBatch.reduce((acc, leaf) => acc + leaf.value, 0),
4872
+ targetAmountSats: targetAmounts?.reduce((acc, amount) => acc + amount, 0) || leavesBatch.reduce((acc, leaf) => acc + leaf.value, 0),
4766
4873
  totalAmountSats: leavesBatch.reduce((acc, leaf) => acc + leaf.value, 0),
4874
+ targetAmountSatsList: targetAmounts,
4767
4875
  // TODO: Request fee from SSP
4768
4876
  feeSats: 0,
4769
4877
  idempotencyKey: uuidv74()
@@ -4819,11 +4927,21 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
4819
4927
  userOutboundTransferExternalId: transfer.id,
4820
4928
  leavesSwapRequestId: request.id
4821
4929
  });
4822
- if (!completeResponse) {
4930
+ if (!completeResponse || !completeResponse.inboundTransfer?.sparkId) {
4823
4931
  throw new Error("Failed to complete leaves swap");
4824
4932
  }
4825
- await this.claimTransfers(40 /* COUNTER_SWAP */);
4826
- return completeResponse;
4933
+ const incomingTransfer = await this.transferService.queryTransfer(
4934
+ completeResponse.inboundTransfer.sparkId
4935
+ );
4936
+ if (!incomingTransfer) {
4937
+ throw new Error("Failed to get incoming transfer");
4938
+ }
4939
+ return await this.claimTransfer({
4940
+ transfer: incomingTransfer,
4941
+ emit: false,
4942
+ retryCount: 0,
4943
+ optimize: false
4944
+ });
4827
4945
  } catch (e) {
4828
4946
  await this.cancelAllSenderInitiatedTransfers();
4829
4947
  throw new Error(`Failed to request leaves swap: ${e}`);
@@ -5360,13 +5478,45 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
5360
5478
  * @returns {Promise<string[]>} The unused deposit addresses
5361
5479
  */
5362
5480
  async getUnusedDepositAddresses() {
5481
+ return (await this.queryAllUnusedDepositAddresses({})).map(
5482
+ (addr) => addr.depositAddress
5483
+ );
5484
+ }
5485
+ /**
5486
+ * Gets all unused deposit addresses for the wallet.
5487
+ *
5488
+ * @param {Object} params - Parameters for querying unused deposit addresses
5489
+ * @param {Uint8Array<ArrayBufferLike>} [params.identityPublicKey] - The identity public key
5490
+ * @param {NetworkProto} [params.network] - The network
5491
+ * @returns {Promise<DepositAddressQueryResult[]>} The unused deposit addresses
5492
+ */
5493
+ async queryAllUnusedDepositAddresses({
5494
+ identityPublicKey,
5495
+ network
5496
+ }) {
5363
5497
  const sparkClient = await this.connectionManager.createSparkClient(
5364
5498
  this.config.getCoordinatorAddress()
5365
5499
  );
5366
- return (await sparkClient.query_unused_deposit_addresses({
5367
- identityPublicKey: await this.config.signer.getIdentityPublicKey(),
5368
- network: NetworkToProto[this.config.getNetwork()]
5369
- })).depositAddresses.map((addr) => addr.depositAddress);
5500
+ let limit = 100;
5501
+ let offset = 0;
5502
+ const pastOffsets = /* @__PURE__ */ new Set();
5503
+ const depositAddresses = [];
5504
+ while (offset >= 0) {
5505
+ if (pastOffsets.has(offset)) {
5506
+ console.warn("Offset has already been seen, stopping");
5507
+ break;
5508
+ }
5509
+ const response = await sparkClient.query_unused_deposit_addresses({
5510
+ identityPublicKey: identityPublicKey ?? await this.config.signer.getIdentityPublicKey(),
5511
+ network: network ?? NetworkToProto[this.config.getNetwork()],
5512
+ limit,
5513
+ offset
5514
+ });
5515
+ depositAddresses.push(...response.depositAddresses);
5516
+ pastOffsets.add(offset);
5517
+ offset = response.offset;
5518
+ }
5519
+ return depositAddresses;
5370
5520
  }
5371
5521
  /**
5372
5522
  * Claims a deposit to the wallet.
@@ -5415,14 +5565,11 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
5415
5565
  });
5416
5566
  }
5417
5567
  const depositTx = getTxFromRawTxHex(txHex);
5418
- const sparkClient = await this.connectionManager.createSparkClient(
5419
- this.config.getCoordinatorAddress()
5420
- );
5421
5568
  const unusedDepositAddresses = new Map(
5422
- (await sparkClient.query_unused_deposit_addresses({
5569
+ (await this.queryAllUnusedDepositAddresses({
5423
5570
  identityPublicKey: await this.config.signer.getIdentityPublicKey(),
5424
5571
  network: NetworkToProto[this.config.getNetwork()]
5425
- })).depositAddresses.map((addr) => [addr.depositAddress, addr])
5572
+ })).map((addr) => [addr.depositAddress, addr])
5426
5573
  );
5427
5574
  let depositAddress;
5428
5575
  let vout = 0;
@@ -5476,14 +5623,11 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
5476
5623
  */
5477
5624
  async advancedDeposit(txHex) {
5478
5625
  const depositTx = getTxFromRawTxHex(txHex);
5479
- const sparkClient = await this.connectionManager.createSparkClient(
5480
- this.config.getCoordinatorAddress()
5481
- );
5482
5626
  const unusedDepositAddresses = new Map(
5483
- (await sparkClient.query_unused_deposit_addresses({
5627
+ (await this.queryAllUnusedDepositAddresses({
5484
5628
  identityPublicKey: await this.config.signer.getIdentityPublicKey(),
5485
5629
  network: NetworkToProto[this.config.getNetwork()]
5486
- })).depositAddresses.map((addr) => [addr.depositAddress, addr])
5630
+ })).map((addr) => [addr.depositAddress, addr])
5487
5631
  );
5488
5632
  let vout = 0;
5489
5633
  const responses = [];
@@ -5587,7 +5731,9 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
5587
5731
  hexToBytes7(receiverAddress.identityPublicKey)
5588
5732
  );
5589
5733
  return await this.withLeaves(async () => {
5590
- let leavesToSend = await this.selectLeaves(amountSats);
5734
+ let leavesToSend = (await this.selectLeaves([amountSats])).get(
5735
+ amountSats
5736
+ );
5591
5737
  leavesToSend = await this.checkRefreshTimelockNodes(leavesToSend);
5592
5738
  leavesToSend = await this.checkExtendTimeLockNodes(leavesToSend);
5593
5739
  const leafKeyTweaks = await Promise.all(
@@ -5830,7 +5976,7 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
5830
5976
  if (type && transfer.type !== type) {
5831
5977
  continue;
5832
5978
  }
5833
- if (transfer.status !== 2 /* TRANSFER_STATUS_SENDER_KEY_TWEAKED */ && transfer.status !== 3 /* TRANSFER_STATUS_RECEIVER_KEY_TWEAKED */ && transfer.status !== 4 /* TRANSFER_STATUSR_RECEIVER_REFUND_SIGNED */ && transfer.status !== 10 /* TRANSFER_STATUS_RECEIVER_KEY_TWEAK_APPLIED */) {
5979
+ 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 */) {
5834
5980
  continue;
5835
5981
  }
5836
5982
  promises.push(
@@ -6089,7 +6235,7 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
6089
6235
  expected: `${totalAmount} sats`
6090
6236
  });
6091
6237
  }
6092
- let leaves = await this.selectLeaves(totalAmount);
6238
+ let leaves = (await this.selectLeaves([totalAmount])).get(totalAmount);
6093
6239
  leaves = await this.checkRefreshTimelockNodes(leaves);
6094
6240
  leaves = await this.checkExtendTimeLockNodes(leaves);
6095
6241
  const leavesToSend = await Promise.all(
@@ -6199,13 +6345,18 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
6199
6345
  *
6200
6346
  * @param {Object} params - Parameters for the withdrawal
6201
6347
  * @param {string} params.onchainAddress - The Bitcoin address where the funds should be sent
6202
- * @param {number} [params.amountSats] - The amount in satoshis to withdraw. If not specified, attempts to withdraw all available funds
6348
+ * @param {CoopExitFeeQuote} params.feeQuote - The fee quote for the withdrawal
6349
+ * @param {ExitSpeed} params.exitSpeed - The exit speed chosen for the withdrawal
6350
+ * @param {number} [params.amountSats] - The amount in satoshis to withdraw. If not specified, attempts to withdraw all available funds and deductFeeFromWithdrawalAmount is set to true.
6351
+ * @param {boolean} [params.deductFeeFromWithdrawalAmount] - Controls how the withdrawal fee is handled. If true, the fee is deducted from the withdrawal amount (amountSats), meaning the recipient will receive amountSats minus the fee. If false, the fee is paid separately from the wallet balance, meaning the recipient will receive the full amountSats.
6203
6352
  * @returns {Promise<CoopExitRequest | null | undefined>} The withdrawal request details, or null/undefined if the request cannot be completed
6204
6353
  */
6205
6354
  async withdraw({
6206
6355
  onchainAddress,
6207
6356
  exitSpeed,
6208
- amountSats
6357
+ feeQuote,
6358
+ amountSats,
6359
+ deductFeeFromWithdrawalAmount = true
6209
6360
  }) {
6210
6361
  if (!Number.isSafeInteger(amountSats)) {
6211
6362
  throw new ValidationError("Sats amount must be less than 2^53", {
@@ -6215,7 +6366,13 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
6215
6366
  });
6216
6367
  }
6217
6368
  return await this.withLeaves(async () => {
6218
- return await this.coopExit(onchainAddress, exitSpeed, amountSats);
6369
+ return await this.coopExit(
6370
+ onchainAddress,
6371
+ feeQuote,
6372
+ exitSpeed,
6373
+ deductFeeFromWithdrawalAmount,
6374
+ amountSats
6375
+ );
6219
6376
  });
6220
6377
  }
6221
6378
  /**
@@ -6226,7 +6383,7 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
6226
6383
  * @returns {Promise<Object | null | undefined>} The exit request details
6227
6384
  * @private
6228
6385
  */
6229
- async coopExit(onchainAddress, exitSpeed, targetAmountSats) {
6386
+ async coopExit(onchainAddress, feeEstimate, exitSpeed, deductFeeFromWithdrawalAmount, targetAmountSats) {
6230
6387
  if (!Number.isSafeInteger(targetAmountSats)) {
6231
6388
  throw new ValidationError("Sats amount must be less than 2^53", {
6232
6389
  field: "targetAmountSats",
@@ -6234,41 +6391,34 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
6234
6391
  expected: "smaller or equal to " + Number.MAX_SAFE_INTEGER
6235
6392
  });
6236
6393
  }
6237
- let leavesToSend = [];
6238
- if (targetAmountSats) {
6239
- leavesToSend = await this.selectLeaves(targetAmountSats);
6240
- } else {
6241
- leavesToSend = this.leaves.map((leaf) => ({
6242
- ...leaf
6243
- }));
6394
+ if (!targetAmountSats) {
6395
+ deductFeeFromWithdrawalAmount = true;
6244
6396
  }
6245
- const sspClient = this.getSspClient();
6246
- const feeEstimate = await sspClient.getCoopExitFeeEstimate({
6247
- leafExternalIds: leavesToSend.map((leaf) => leaf.id),
6248
- withdrawalAddress: onchainAddress
6249
- });
6250
- if (feeEstimate) {
6251
- let fee;
6252
- switch (exitSpeed) {
6253
- case ExitSpeed_default.FAST:
6254
- fee = (feeEstimate.speedFast?.l1BroadcastFee.originalValue || 0) + (feeEstimate.speedFast?.userFee.originalValue || 0);
6255
- break;
6256
- case ExitSpeed_default.MEDIUM:
6257
- fee = (feeEstimate.speedMedium?.l1BroadcastFee.originalValue || 0) + (feeEstimate.speedMedium?.userFee.originalValue || 0);
6258
- break;
6259
- case ExitSpeed_default.SLOW:
6260
- fee = (feeEstimate.speedSlow?.l1BroadcastFee.originalValue || 0) + (feeEstimate.speedSlow?.userFee.originalValue || 0);
6261
- break;
6262
- default:
6263
- throw new ValidationError("Invalid exit speed", {
6264
- field: "exitSpeed",
6265
- value: exitSpeed,
6266
- expected: "FAST, MEDIUM, or SLOW"
6267
- });
6268
- }
6269
- if (fee !== void 0 && fee > leavesToSend.reduce((acc, leaf) => acc + leaf.value, 0)) {
6397
+ let fee;
6398
+ switch (exitSpeed) {
6399
+ case ExitSpeed_default.FAST:
6400
+ fee = (feeEstimate.l1BroadcastFeeFast?.originalValue || 0) + (feeEstimate.userFeeFast?.originalValue || 0);
6401
+ break;
6402
+ case ExitSpeed_default.MEDIUM:
6403
+ fee = (feeEstimate.l1BroadcastFeeMedium?.originalValue || 0) + (feeEstimate.userFeeMedium?.originalValue || 0);
6404
+ break;
6405
+ case ExitSpeed_default.SLOW:
6406
+ fee = (feeEstimate.l1BroadcastFeeSlow?.originalValue || 0) + (feeEstimate.userFeeSlow?.originalValue || 0);
6407
+ break;
6408
+ default:
6409
+ throw new ValidationError("Invalid exit speed", {
6410
+ field: "exitSpeed",
6411
+ value: exitSpeed,
6412
+ expected: "FAST, MEDIUM, or SLOW"
6413
+ });
6414
+ }
6415
+ let leavesToSendToSsp = [];
6416
+ let leavesToSendToSE = [];
6417
+ if (deductFeeFromWithdrawalAmount) {
6418
+ leavesToSendToSsp = targetAmountSats ? (await this.selectLeaves([targetAmountSats])).get(targetAmountSats) : this.leaves;
6419
+ if (fee > leavesToSendToSsp.reduce((acc, leaf) => acc + leaf.value, 0)) {
6270
6420
  throw new ValidationError(
6271
- "The fee for the withdrawal is greater than the target amount",
6421
+ "The fee for the withdrawal is greater than the target withdrawal amount",
6272
6422
  {
6273
6423
  field: "fee",
6274
6424
  value: fee,
@@ -6276,11 +6426,32 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
6276
6426
  }
6277
6427
  );
6278
6428
  }
6429
+ } else {
6430
+ if (!targetAmountSats) {
6431
+ throw new ValidationError(
6432
+ "targetAmountSats is required when deductFeeFromWithdrawalAmount is false",
6433
+ {
6434
+ field: "targetAmountSats",
6435
+ value: targetAmountSats,
6436
+ expected: "defined when deductFeeFromWithdrawalAmount is false"
6437
+ }
6438
+ );
6439
+ }
6440
+ const leaves = await this.selectLeaves([targetAmountSats, fee]);
6441
+ const leavesForTargetAmount = leaves.get(targetAmountSats);
6442
+ const leavesForFee = leaves.get(fee);
6443
+ if (!leavesForTargetAmount || !leavesForFee) {
6444
+ throw new Error("Failed to select leaves for target amount and fee");
6445
+ }
6446
+ leavesToSendToSsp = leavesForTargetAmount;
6447
+ leavesToSendToSE = leavesForFee;
6279
6448
  }
6280
- leavesToSend = await this.checkRefreshTimelockNodes(leavesToSend);
6281
- leavesToSend = await this.checkExtendTimeLockNodes(leavesToSend);
6449
+ leavesToSendToSsp = await this.checkRefreshTimelockNodes(leavesToSendToSsp);
6450
+ leavesToSendToSsp = await this.checkExtendTimeLockNodes(leavesToSendToSsp);
6451
+ leavesToSendToSE = await this.checkRefreshTimelockNodes(leavesToSendToSE);
6452
+ leavesToSendToSE = await this.checkExtendTimeLockNodes(leavesToSendToSE);
6282
6453
  const leafKeyTweaks = await Promise.all(
6283
- leavesToSend.map(async (leaf) => ({
6454
+ [...leavesToSendToSE, ...leavesToSendToSsp].map(async (leaf) => ({
6284
6455
  leaf,
6285
6456
  signingPubKey: await this.config.signer.generatePublicKey(
6286
6457
  sha2567(leaf.id)
@@ -6288,13 +6459,23 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
6288
6459
  newSigningPubKey: await this.config.signer.generatePublicKey()
6289
6460
  }))
6290
6461
  );
6291
- const coopExitRequest = await sspClient.requestCoopExit({
6292
- leafExternalIds: leavesToSend.map((leaf) => leaf.id),
6462
+ const requestCoopExitParams = {
6463
+ leafExternalIds: leavesToSendToSsp.map((leaf) => leaf.id),
6293
6464
  withdrawalAddress: onchainAddress,
6294
6465
  idempotencyKey: uuidv74(),
6295
6466
  exitSpeed,
6296
- withdrawAll: true
6297
- });
6467
+ withdrawAll: deductFeeFromWithdrawalAmount
6468
+ };
6469
+ if (!deductFeeFromWithdrawalAmount) {
6470
+ requestCoopExitParams.feeQuoteId = feeEstimate.id;
6471
+ requestCoopExitParams.feeLeafExternalIds = leavesToSendToSE.map(
6472
+ (leaf) => leaf.id
6473
+ );
6474
+ }
6475
+ const sspClient = this.getSspClient();
6476
+ const coopExitRequest = await sspClient.requestCoopExit(
6477
+ requestCoopExitParams
6478
+ );
6298
6479
  if (!coopExitRequest?.rawConnectorTransaction) {
6299
6480
  throw new Error("Failed to request coop exit");
6300
6481
  }
@@ -6332,9 +6513,9 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
6332
6513
  * @param {Object} params - Input parameters for fee estimation
6333
6514
  * @param {number} params.amountSats - The amount in satoshis to withdraw
6334
6515
  * @param {string} params.withdrawalAddress - The Bitcoin address where the funds should be sent
6335
- * @returns {Promise<CoopExitFeeEstimatesOutput | null>} Fee estimate for the withdrawal
6516
+ * @returns {Promise<CoopExitFeeQuote | null>} Fee estimate for the withdrawal
6336
6517
  */
6337
- async getWithdrawalFeeEstimate({
6518
+ async getWithdrawalFeeQuote({
6338
6519
  amountSats,
6339
6520
  withdrawalAddress
6340
6521
  }) {
@@ -6346,10 +6527,10 @@ var SparkWallet = class _SparkWallet extends EventEmitter {
6346
6527
  expected: "smaller or equal to " + Number.MAX_SAFE_INTEGER
6347
6528
  });
6348
6529
  }
6349
- let leaves = await this.selectLeaves(amountSats);
6530
+ let leaves = (await this.selectLeaves([amountSats])).get(amountSats);
6350
6531
  leaves = await this.checkRefreshTimelockNodes(leaves);
6351
6532
  leaves = await this.checkExtendTimeLockNodes(leaves);
6352
- const feeEstimate = await sspClient.getCoopExitFeeEstimate({
6533
+ const feeEstimate = await sspClient.getCoopExitFeeQuote({
6353
6534
  leafExternalIds: leaves.map((leaf) => leaf.id),
6354
6535
  withdrawalAddress
6355
6536
  });