@arkade-os/boltz-swap 0.3.32 → 0.3.34

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 (31) hide show
  1. package/README.md +22 -26
  2. package/dist/{arkade-swaps-CS8FZSVL.d.cts → arkade-swaps-BXAD1s8j.d.ts} +69 -8
  3. package/dist/{arkade-swaps-WiKCanCL.d.ts → arkade-swaps-CfMets16.d.cts} +69 -8
  4. package/dist/{chunk-NHBWNN6H.js → chunk-5K2FS2FE.js} +8 -27
  5. package/dist/chunk-SJQJQO7P.js +25 -0
  6. package/dist/{chunk-B3Q4TFWT.js → chunk-TDBUZE4N.js} +817 -866
  7. package/dist/expo/background.cjs +840 -892
  8. package/dist/expo/background.d.cts +3 -3
  9. package/dist/expo/background.d.ts +3 -3
  10. package/dist/expo/background.js +9 -20
  11. package/dist/expo/index.cjs +840 -870
  12. package/dist/expo/index.d.cts +8 -6
  13. package/dist/expo/index.d.ts +8 -6
  14. package/dist/expo/index.js +17 -20
  15. package/dist/index.cjs +1034 -1022
  16. package/dist/index.d.cts +95 -11
  17. package/dist/index.d.ts +95 -11
  18. package/dist/index.js +164 -119
  19. package/dist/repositories/realm/index.cjs +10 -22
  20. package/dist/repositories/realm/index.d.cts +7 -5
  21. package/dist/repositories/realm/index.d.ts +7 -5
  22. package/dist/repositories/realm/index.js +8 -22
  23. package/dist/repositories/sqlite/index.cjs +12 -23
  24. package/dist/repositories/sqlite/index.d.cts +1 -1
  25. package/dist/repositories/sqlite/index.d.ts +1 -1
  26. package/dist/repositories/sqlite/index.js +10 -23
  27. package/dist/{swapsPollProcessor-BF3uTFae.d.cts → swapsPollProcessor-BpAqG0V6.d.cts} +3 -3
  28. package/dist/{swapsPollProcessor-wYOMzldd.d.ts → swapsPollProcessor-DFVOAy_-.d.ts} +3 -3
  29. package/dist/{types-BBI7-KJ0.d.cts → types--axEWA8c.d.cts} +42 -4
  30. package/dist/{types-BBI7-KJ0.d.ts → types--axEWA8c.d.ts} +42 -4
  31. package/package.json +10 -25
package/dist/index.cjs CHANGED
@@ -35,12 +35,14 @@ __export(index_exports, {
35
35
  ArkadeSwapsMessageHandler: () => ArkadeSwapsMessageHandler,
36
36
  BoltzRefundError: () => BoltzRefundError,
37
37
  BoltzSwapProvider: () => BoltzSwapProvider,
38
+ InMemorySwapRepository: () => InMemorySwapRepository,
38
39
  IndexedDbSwapRepository: () => IndexedDbSwapRepository,
39
40
  InsufficientFundsError: () => InsufficientFundsError,
40
41
  InvoiceExpiredError: () => InvoiceExpiredError,
41
42
  InvoiceFailedToPayError: () => InvoiceFailedToPayError,
42
43
  NetworkError: () => NetworkError,
43
44
  PreimageFetchError: () => PreimageFetchError,
45
+ QuoteRejectedError: () => QuoteRejectedError,
44
46
  SchemaError: () => SchemaError,
45
47
  ServiceWorkerArkadeLightning: () => ServiceWorkerArkadeSwaps,
46
48
  ServiceWorkerArkadeSwaps: () => ServiceWorkerArkadeSwaps,
@@ -99,7 +101,10 @@ var SwapError = class extends Error {
99
101
  /** The pending swap associated with this error, if available. */
100
102
  pendingSwap;
101
103
  constructor(options = {}) {
102
- super(options.message ?? "Error during swap.");
104
+ super(
105
+ options.message ?? "Error during swap.",
106
+ options.cause !== void 0 ? { cause: options.cause } : void 0
107
+ );
103
108
  this.name = "SwapError";
104
109
  this.isClaimable = options.isClaimable ?? false;
105
110
  this.isRefundable = options.isRefundable ?? false;
@@ -191,6 +196,100 @@ var TransactionRefundedError = class extends SwapError {
191
196
  this.name = "TransactionRefundedError";
192
197
  }
193
198
  };
199
+ var QuoteRejectedError = class _QuoteRejectedError extends SwapError {
200
+ reason;
201
+ quotedAmount;
202
+ floor;
203
+ constructor(options) {
204
+ super({
205
+ message: options.message ?? _QuoteRejectedError.defaultMessage(options),
206
+ ...options
207
+ });
208
+ this.name = "QuoteRejectedError";
209
+ this.reason = options.reason;
210
+ this.quotedAmount = "quotedAmount" in options ? options.quotedAmount : void 0;
211
+ this.floor = "floor" in options ? options.floor : void 0;
212
+ }
213
+ static defaultMessage(options) {
214
+ switch (options.reason) {
215
+ case "below_floor":
216
+ return `Boltz quote ${options.quotedAmount} is below acceptable floor ${options.floor}`;
217
+ case "non_positive":
218
+ return `Boltz quote ${options.quotedAmount} is not positive`;
219
+ case "non_safe_integer":
220
+ return `Boltz quote ${options.quotedAmount} is not a safe positive satoshi integer`;
221
+ case "no_baseline":
222
+ return "Cannot accept quote: no minAcceptableAmount and no stored pending swap";
223
+ }
224
+ }
225
+ /**
226
+ * Serialize into a plain `Error` whose `.message` carries the full
227
+ * rejection payload as JSON behind a marker prefix. Structured clone
228
+ * (used by `postMessage` between page and service worker) preserves
229
+ * `Error.message` reliably but strips custom `.name` and own properties,
230
+ * so we move the typed data into the message field for transport.
231
+ */
232
+ toTransportError() {
233
+ return new Error(
234
+ QUOTE_REJECTION_TRANSPORT_PREFIX + JSON.stringify({
235
+ reason: this.reason,
236
+ message: this.message,
237
+ quotedAmount: this.quotedAmount,
238
+ floor: this.floor
239
+ })
240
+ );
241
+ }
242
+ /**
243
+ * Inverse of `toTransportError`. Returns a real `QuoteRejectedError` if
244
+ * `error` carries the transport prefix, else `null`.
245
+ */
246
+ static fromTransportError(error) {
247
+ if (!(error instanceof Error) || !error.message.startsWith(QUOTE_REJECTION_TRANSPORT_PREFIX)) {
248
+ return null;
249
+ }
250
+ const payload = error.message.slice(QUOTE_REJECTION_TRANSPORT_PREFIX.length);
251
+ let data;
252
+ try {
253
+ data = JSON.parse(payload);
254
+ } catch {
255
+ return null;
256
+ }
257
+ if (typeof data.reason !== "string" || !QUOTE_REJECTION_REASONS.has(data.reason)) {
258
+ return null;
259
+ }
260
+ const message = typeof data.message === "string" ? data.message : void 0;
261
+ const reason = data.reason;
262
+ const quotedAmount = typeof data.quotedAmount === "number" ? data.quotedAmount : null;
263
+ const floor = typeof data.floor === "number" ? data.floor : null;
264
+ switch (reason) {
265
+ case "below_floor":
266
+ if (quotedAmount === null || floor === null) return null;
267
+ return new _QuoteRejectedError({
268
+ reason,
269
+ quotedAmount,
270
+ floor,
271
+ message
272
+ });
273
+ case "non_positive":
274
+ case "non_safe_integer":
275
+ if (quotedAmount === null) return null;
276
+ return new _QuoteRejectedError({
277
+ reason,
278
+ quotedAmount,
279
+ message
280
+ });
281
+ case "no_baseline":
282
+ return new _QuoteRejectedError({ reason, message });
283
+ }
284
+ }
285
+ };
286
+ var QUOTE_REJECTION_TRANSPORT_PREFIX = "QUOTE_REJECTED::";
287
+ var QUOTE_REJECTION_REASONS = /* @__PURE__ */ new Set([
288
+ "below_floor",
289
+ "non_positive",
290
+ "non_safe_integer",
291
+ "no_baseline"
292
+ ]);
194
293
  var BoltzRefundError = class extends Error {
195
294
  constructor(message, cause) {
196
295
  super(message);
@@ -206,18 +305,10 @@ var import_sdk8 = require("@arkade-os/sdk");
206
305
  var import_sdk = require("@arkade-os/sdk");
207
306
  var import_base = require("@scure/base");
208
307
  var isSubmarineFailedStatus = (status) => {
209
- return [
210
- "invoice.failedToPay",
211
- "transaction.lockupFailed",
212
- "swap.expired"
213
- ].includes(status);
308
+ return ["invoice.failedToPay", "transaction.lockupFailed", "swap.expired"].includes(status);
214
309
  };
215
310
  var isSubmarineFinalStatus = (status) => {
216
- return [
217
- "invoice.failedToPay",
218
- "transaction.claimed",
219
- "swap.expired"
220
- ].includes(status);
311
+ return ["invoice.failedToPay", "transaction.claimed", "swap.expired"].includes(status);
221
312
  };
222
313
  var isSubmarinePendingStatus = (status) => {
223
314
  return [
@@ -231,11 +322,7 @@ var isSubmarinePendingStatus = (status) => {
231
322
  ].includes(status);
232
323
  };
233
324
  var isSubmarineRefundableStatus = (status) => {
234
- return [
235
- "invoice.failedToPay",
236
- "transaction.lockupFailed",
237
- "swap.expired"
238
- ].includes(status);
325
+ return ["invoice.failedToPay", "transaction.lockupFailed", "swap.expired"].includes(status);
239
326
  };
240
327
  var isSubmarineSuccessStatus = (status) => {
241
328
  return status === "transaction.claimed";
@@ -259,11 +346,7 @@ var isReverseFinalStatus = (status) => {
259
346
  ].includes(status);
260
347
  };
261
348
  var isReversePendingStatus = (status) => {
262
- return [
263
- "swap.created",
264
- "transaction.mempool",
265
- "transaction.confirmed"
266
- ].includes(status);
349
+ return ["swap.created", "transaction.mempool", "transaction.confirmed"].includes(status);
267
350
  };
268
351
  var isReverseClaimableStatus = (status) => {
269
352
  return ["transaction.mempool", "transaction.confirmed"].includes(status);
@@ -275,10 +358,7 @@ var isChainFailedStatus = (status) => {
275
358
  return ["transaction.failed", "swap.expired"].includes(status);
276
359
  };
277
360
  var isChainClaimableStatus = (status) => {
278
- return [
279
- "transaction.server.mempool",
280
- "transaction.server.confirmed"
281
- ].includes(status);
361
+ return ["transaction.server.mempool", "transaction.server.confirmed"].includes(status);
282
362
  };
283
363
  var isChainFinalStatus = (status) => {
284
364
  return [
@@ -417,8 +497,7 @@ var BASE_URLS = {
417
497
  var isSwapNotFoundBody = (error) => {
418
498
  const needle = "could not find swap";
419
499
  const fromJson = error.errorData?.error;
420
- if (typeof fromJson === "string" && fromJson.toLowerCase().includes(needle))
421
- return true;
500
+ if (typeof fromJson === "string" && fromJson.toLowerCase().includes(needle)) return true;
422
501
  return error.message.toLowerCase().includes(needle);
423
502
  };
424
503
  var BoltzSwapProvider = class {
@@ -432,10 +511,7 @@ var BoltzSwapProvider = class {
432
511
  this.network = config.network;
433
512
  this.referralId = config.referralId ?? "arkade-ts-sdk";
434
513
  const apiUrl = config.apiUrl || BASE_URLS[config.network];
435
- if (!apiUrl)
436
- throw new Error(
437
- `API URL is required for network: ${config.network}`
438
- );
514
+ if (!apiUrl) throw new Error(`API URL is required for network: ${config.network}`);
439
515
  this.apiUrl = apiUrl;
440
516
  this.wsUrl = this.apiUrl.replace(/^http(s)?:\/\//, "ws$1://").replace("9069", "9004") + "/v2/ws";
441
517
  }
@@ -454,10 +530,7 @@ var BoltzSwapProvider = class {
454
530
  /** Returns current Lightning swap fees (submarine + reverse) from Boltz. */
455
531
  async getFees() {
456
532
  const [submarine, reverse] = await Promise.all([
457
- this.request(
458
- "/v2/swap/submarine",
459
- "GET"
460
- ),
533
+ this.request("/v2/swap/submarine", "GET"),
461
534
  this.request("/v2/swap/reverse", "GET")
462
535
  ]);
463
536
  if (!isGetSubmarinePairsResponse(submarine))
@@ -477,10 +550,7 @@ var BoltzSwapProvider = class {
477
550
  }
478
551
  /** Returns current Lightning swap min/max limits from Boltz. */
479
552
  async getLimits() {
480
- const response = await this.request(
481
- "/v2/swap/submarine",
482
- "GET"
483
- );
553
+ const response = await this.request("/v2/swap/submarine", "GET");
484
554
  if (!isGetSubmarinePairsResponse(response))
485
555
  throw new SchemaError({ message: "error fetching limits" });
486
556
  return {
@@ -490,10 +560,7 @@ var BoltzSwapProvider = class {
490
560
  }
491
561
  /** Returns the current BTC chain tip height from Boltz. */
492
562
  async getChainHeight() {
493
- const response = await this.request(
494
- "/v2/chain/heights",
495
- "GET"
496
- );
563
+ const response = await this.request("/v2/chain/heights", "GET");
497
564
  if (typeof response?.BTC !== "number")
498
565
  throw new SchemaError({
499
566
  message: "error fetching chain heights"
@@ -523,10 +590,7 @@ var BoltzSwapProvider = class {
523
590
  async getSwapStatus(id) {
524
591
  let response;
525
592
  try {
526
- response = await this.request(
527
- `/v2/swap/${id}`,
528
- "GET"
529
- );
593
+ response = await this.request(`/v2/swap/${id}`, "GET");
530
594
  } catch (error) {
531
595
  if (error instanceof NetworkError && error.statusCode === 404 && isSwapNotFoundBody(error)) {
532
596
  throw new SwapNotFoundError(id, error.errorData);
@@ -622,12 +686,10 @@ var BoltzSwapProvider = class {
622
686
  throw new SwapError({ message: "Invalid 'to' chain" });
623
687
  if (["BTC", "ARK"].indexOf(from) === -1)
624
688
  throw new SwapError({ message: "Invalid 'from' chain" });
625
- if (to === from)
626
- throw new SwapError({ message: "Invalid swap direction" });
689
+ if (to === from) throw new SwapError({ message: "Invalid swap direction" });
627
690
  if (!preimageHash || preimageHash.length != 64)
628
691
  throw new SwapError({ message: "Invalid preimageHash" });
629
- if (feeSatsPerByte <= 0)
630
- throw new SwapError({ message: "Invalid feeSatsPerByte" });
692
+ if (feeSatsPerByte <= 0) throw new SwapError({ message: "Invalid feeSatsPerByte" });
631
693
  if (serverLockAmount !== void 0 && userLockAmount !== void 0 || serverLockAmount === void 0 && userLockAmount === void 0)
632
694
  throw new SwapError({
633
695
  message: "Either serverLockAmount or userLockAmount must be provided"
@@ -682,12 +744,8 @@ var BoltzSwapProvider = class {
682
744
  message: "Error refunding submarine swap"
683
745
  });
684
746
  return {
685
- transaction: import_sdk.Transaction.fromPSBT(
686
- import_base.base64.decode(response.transaction)
687
- ),
688
- checkpoint: import_sdk.Transaction.fromPSBT(
689
- import_base.base64.decode(response.checkpoint)
690
- )
747
+ transaction: import_sdk.Transaction.fromPSBT(import_base.base64.decode(response.transaction)),
748
+ checkpoint: import_sdk.Transaction.fromPSBT(import_base.base64.decode(response.checkpoint))
691
749
  };
692
750
  }
693
751
  /** Requests Boltz co-signature for a chain swap refund. Returns signed transaction + checkpoint. */
@@ -706,53 +764,53 @@ var BoltzSwapProvider = class {
706
764
  message: "Error refunding chain swap"
707
765
  });
708
766
  return {
709
- transaction: import_sdk.Transaction.fromPSBT(
710
- import_base.base64.decode(response.transaction)
711
- ),
712
- checkpoint: import_sdk.Transaction.fromPSBT(
713
- import_base.base64.decode(response.checkpoint)
714
- )
767
+ transaction: import_sdk.Transaction.fromPSBT(import_base.base64.decode(response.transaction)),
768
+ checkpoint: import_sdk.Transaction.fromPSBT(import_base.base64.decode(response.checkpoint))
715
769
  };
716
770
  }
717
- /** Monitors swap status updates via WebSocket. Calls update callback on each status change. Resolves when terminal. */
771
+ /**
772
+ * Monitors swap status updates and forwards them to the update callback.
773
+ * Prefers a WebSocket subscription; on connection error or premature close
774
+ * it falls back to REST polling so callers don't fail when the WS endpoint
775
+ * is flaky (observed in CI). Resolves when the swap reaches a terminal
776
+ * status.
777
+ */
718
778
  async monitorSwap(swapId, update) {
719
779
  return new Promise((resolve, reject) => {
720
- const webSocket = new globalThis.WebSocket(this.wsUrl);
721
- const connectionTimeout = setTimeout(() => {
722
- webSocket.close();
723
- reject(new NetworkError("WebSocket connection timeout"));
724
- }, 3e4);
725
- webSocket.onerror = (error) => {
726
- clearTimeout(connectionTimeout);
727
- reject(
728
- new NetworkError(
729
- `WebSocket error: ${error.message}`
730
- )
731
- );
732
- };
733
- webSocket.onopen = () => {
734
- clearTimeout(connectionTimeout);
735
- webSocket.send(
736
- JSON.stringify({
737
- op: "subscribe",
738
- channel: "swap.update",
739
- args: [swapId]
740
- })
741
- );
780
+ let settled = false;
781
+ let lastStatus = null;
782
+ let pollTimer = null;
783
+ let webSocket = null;
784
+ let connectionTimeout = null;
785
+ const cleanup = () => {
786
+ if (connectionTimeout) {
787
+ clearTimeout(connectionTimeout);
788
+ connectionTimeout = null;
789
+ }
790
+ if (pollTimer) {
791
+ clearInterval(pollTimer);
792
+ pollTimer = null;
793
+ }
794
+ if (webSocket) {
795
+ try {
796
+ webSocket.close();
797
+ } catch {
798
+ }
799
+ webSocket = null;
800
+ }
742
801
  };
743
- webSocket.onclose = () => {
744
- clearTimeout(connectionTimeout);
745
- resolve();
802
+ const finish = (err) => {
803
+ if (settled) return;
804
+ settled = true;
805
+ cleanup();
806
+ if (err) reject(err);
807
+ else resolve();
746
808
  };
747
- webSocket.onmessage = async (rawMsg) => {
748
- const msg = JSON.parse(rawMsg.data);
749
- if (msg.event !== "update" || msg.args[0].id !== swapId) return;
750
- if (msg.args[0].error) {
751
- webSocket.close();
752
- reject(new SwapError({ message: msg.args[0].error }));
753
- }
754
- const status = msg.args[0].status;
755
- const negotiable = status === "transaction.lockupFailed" && msg.args[0].failureDetails?.actual !== void 0 && msg.args[0].failureDetails?.expected !== void 0;
809
+ const handleStatus = (status, data) => {
810
+ if (settled) return;
811
+ if (status === lastStatus) return;
812
+ lastStatus = status;
813
+ const negotiable = status === "transaction.lockupFailed" && data?.failureDetails?.actual !== void 0 && data?.failureDetails?.expected !== void 0;
756
814
  switch (status) {
757
815
  case "invoice.settled":
758
816
  case "transaction.claimed":
@@ -761,13 +819,13 @@ var BoltzSwapProvider = class {
761
819
  case "invoice.failedToPay":
762
820
  case "transaction.failed":
763
821
  case "swap.expired":
764
- webSocket.close();
765
- update(status, msg.args[0]);
766
- break;
822
+ update(status, data);
823
+ finish();
824
+ return;
767
825
  case "transaction.lockupFailed":
768
- if (!negotiable) webSocket.close();
769
- update(status, msg.args[0]);
770
- break;
826
+ update(status, data);
827
+ if (!negotiable) finish();
828
+ return;
771
829
  case "invoice.paid":
772
830
  case "invoice.pending":
773
831
  case "invoice.set":
@@ -777,9 +835,77 @@ var BoltzSwapProvider = class {
777
835
  case "transaction.claim.pending":
778
836
  case "transaction.server.mempool":
779
837
  case "transaction.server.confirmed":
780
- update(status, msg.args[0]);
838
+ update(status, data);
839
+ return;
781
840
  }
782
841
  };
842
+ const startPolling = () => {
843
+ if (settled || pollTimer) return;
844
+ const poll = async () => {
845
+ if (settled) return;
846
+ try {
847
+ const result = await this.getSwapStatus(swapId);
848
+ handleStatus(result.status, { ...result, id: swapId });
849
+ } catch {
850
+ }
851
+ };
852
+ pollTimer = setInterval(poll, 1e3);
853
+ poll();
854
+ };
855
+ try {
856
+ webSocket = new globalThis.WebSocket(this.wsUrl);
857
+ connectionTimeout = setTimeout(() => {
858
+ try {
859
+ webSocket?.close();
860
+ } catch {
861
+ }
862
+ webSocket = null;
863
+ startPolling();
864
+ }, 5e3);
865
+ webSocket.onerror = () => {
866
+ if (connectionTimeout) {
867
+ clearTimeout(connectionTimeout);
868
+ connectionTimeout = null;
869
+ }
870
+ webSocket = null;
871
+ startPolling();
872
+ };
873
+ webSocket.onopen = () => {
874
+ if (connectionTimeout) {
875
+ clearTimeout(connectionTimeout);
876
+ connectionTimeout = null;
877
+ }
878
+ webSocket?.send(
879
+ JSON.stringify({
880
+ op: "subscribe",
881
+ channel: "swap.update",
882
+ args: [swapId]
883
+ })
884
+ );
885
+ };
886
+ webSocket.onclose = () => {
887
+ if (connectionTimeout) {
888
+ clearTimeout(connectionTimeout);
889
+ connectionTimeout = null;
890
+ }
891
+ if (!settled && !pollTimer) startPolling();
892
+ };
893
+ webSocket.onmessage = (rawMsg) => {
894
+ try {
895
+ const msg = JSON.parse(rawMsg.data);
896
+ if (msg.event !== "update" || msg.args?.[0]?.id !== swapId) return;
897
+ if (msg.args[0].error) {
898
+ finish(new SwapError({ message: msg.args[0].error }));
899
+ return;
900
+ }
901
+ handleStatus(msg.args[0].status, msg.args[0]);
902
+ } catch {
903
+ }
904
+ };
905
+ } catch {
906
+ webSocket = null;
907
+ startPolling();
908
+ }
783
909
  });
784
910
  }
785
911
  /** Returns current chain swap fees for a given pair (e.g. ARK→BTC). */
@@ -787,10 +913,7 @@ var BoltzSwapProvider = class {
787
913
  if (from === to) {
788
914
  throw new SwapError({ message: "Invalid chain pair" });
789
915
  }
790
- const response = await this.request(
791
- "/v2/swap/chain",
792
- "GET"
793
- );
916
+ const response = await this.request("/v2/swap/chain", "GET");
794
917
  if (!isGetChainPairsResponse(response))
795
918
  throw new SchemaError({ message: "error fetching fees" });
796
919
  if (!response[from]?.[to]) {
@@ -805,10 +928,7 @@ var BoltzSwapProvider = class {
805
928
  if (from === to) {
806
929
  throw new SwapError({ message: "Invalid chain pair" });
807
930
  }
808
- const response = await this.request(
809
- "/v2/swap/chain",
810
- "GET"
811
- );
931
+ const response = await this.request("/v2/swap/chain", "GET");
812
932
  if (!isGetChainPairsResponse(response))
813
933
  throw new SchemaError({ message: "error fetching limits" });
814
934
  if (!response[from]?.[to]) {
@@ -937,9 +1057,7 @@ var BoltzSwapProvider = class {
937
1057
  return await response.json();
938
1058
  } catch (error) {
939
1059
  if (error instanceof NetworkError) throw error;
940
- throw new NetworkError(
941
- `Request to ${url} failed: ${error.message}`
942
- );
1060
+ throw new NetworkError(`Request to ${url} failed: ${error.message}`);
943
1061
  }
944
1062
  }
945
1063
  };
@@ -961,8 +1079,7 @@ var findKeyIndex = (keys, target) => keys.findIndex((k) => (0, import_utils.equa
961
1079
  var assertPublicKeys = (keys) => {
962
1080
  const seen = /* @__PURE__ */ new Set();
963
1081
  for (const key of keys) {
964
- if (key.length !== 33)
965
- throw new Error(`public key must be 33 bytes, got ${key.length}`);
1082
+ if (key.length !== 33) throw new Error(`public key must be 33 bytes, got ${key.length}`);
966
1083
  const enc = import_base2.hex.encode(key);
967
1084
  if (seen.has(enc)) throw new Error(`duplicate public key ${enc}`);
968
1085
  seen.add(enc);
@@ -970,9 +1087,7 @@ var assertPublicKeys = (keys) => {
970
1087
  };
971
1088
  var aggregateKeys = (publicKeys, tweak) => {
972
1089
  assertPublicKeys([...publicKeys]);
973
- return (0, import_musig2.keyAggExport)(
974
- (0, import_musig2.keyAggregate)([...publicKeys], tweak ? [tweak] : [], tweak ? [true] : [])
975
- );
1090
+ return (0, import_musig2.keyAggExport)((0, import_musig2.keyAggregate)([...publicKeys], tweak ? [tweak] : [], tweak ? [true] : []));
976
1091
  };
977
1092
  var MusigKeyAgg = class _MusigKeyAgg {
978
1093
  constructor(privateKey, myPublicKey, publicKeys, myIndex, aggPubkey, internalKey, _tweak) {
@@ -1019,18 +1134,12 @@ var MusigWithMessage = class {
1019
1134
  this.msg = msg;
1020
1135
  }
1021
1136
  generateNonce() {
1022
- const nonce = (0, import_musig2.nonceGen)(
1023
- this.myPublicKey,
1024
- this.privateKey,
1025
- this.aggPubkey,
1026
- this.msg
1027
- );
1137
+ const nonce = (0, import_musig2.nonceGen)(this.myPublicKey, this.privateKey, this.aggPubkey, this.msg);
1028
1138
  return new MusigWithNonce(
1029
1139
  this.privateKey,
1030
1140
  this.myPublicKey,
1031
1141
  this.publicKeys,
1032
1142
  this.myIndex,
1033
- this.aggPubkey,
1034
1143
  this.tweak,
1035
1144
  this.msg,
1036
1145
  nonce
@@ -1038,12 +1147,11 @@ var MusigWithMessage = class {
1038
1147
  }
1039
1148
  };
1040
1149
  var MusigWithNonce = class {
1041
- constructor(privateKey, myPublicKey, publicKeys, myIndex, aggPubkey, tweak, msg, nonce) {
1150
+ constructor(privateKey, myPublicKey, publicKeys, myIndex, tweak, msg, nonce) {
1042
1151
  this.privateKey = privateKey;
1043
1152
  this.myPublicKey = myPublicKey;
1044
1153
  this.publicKeys = publicKeys;
1045
1154
  this.myIndex = myIndex;
1046
- this.aggPubkey = aggPubkey;
1047
1155
  this.tweak = tweak;
1048
1156
  this.msg = msg;
1049
1157
  this.nonce = nonce;
@@ -1075,10 +1183,8 @@ var MusigWithNonce = class {
1075
1183
  const aggregatedNonce = (0, import_musig2.nonceAggregate)([...ordered]);
1076
1184
  return new MusigNoncesAggregated(
1077
1185
  this.privateKey,
1078
- this.myPublicKey,
1079
1186
  this.publicKeys,
1080
1187
  this.myIndex,
1081
- this.aggPubkey,
1082
1188
  this.tweak,
1083
1189
  this.msg,
1084
1190
  this.nonce,
@@ -1088,12 +1194,10 @@ var MusigWithNonce = class {
1088
1194
  }
1089
1195
  };
1090
1196
  var MusigNoncesAggregated = class {
1091
- constructor(privateKey, myPublicKey, publicKeys, myIndex, aggPubkey, tweak, msg, nonce, pubNonces, aggregatedNonce) {
1197
+ constructor(privateKey, publicKeys, myIndex, tweak, msg, nonce, pubNonces, aggregatedNonce) {
1092
1198
  this.privateKey = privateKey;
1093
- this.myPublicKey = myPublicKey;
1094
1199
  this.publicKeys = publicKeys;
1095
1200
  this.myIndex = myIndex;
1096
- this.aggPubkey = aggPubkey;
1097
1201
  this.tweak = tweak;
1098
1202
  this.msg = msg;
1099
1203
  this.nonce = nonce;
@@ -1139,11 +1243,7 @@ var MusigSession = class {
1139
1243
  const index = typeof publicKeyOrIndex === "number" ? publicKeyOrIndex : findKeyIndex(this.publicKeys, publicKeyOrIndex);
1140
1244
  if (index < 0 || index >= this.publicKeys.length)
1141
1245
  throw new Error("public key not found or index out of range");
1142
- if (!this.session.partialSigVerify(
1143
- signature,
1144
- [...this.pubNonces],
1145
- index
1146
- )) {
1246
+ if (!this.session.partialSigVerify(signature, [...this.pubNonces], index)) {
1147
1247
  throw new Error("invalid partial signature");
1148
1248
  }
1149
1249
  this.partialSignatures[index] = signature;
@@ -1152,12 +1252,7 @@ var MusigSession = class {
1152
1252
  signPartial() {
1153
1253
  const sig = this.session.sign(this.nonce.secret, this.privateKey, true);
1154
1254
  this.partialSignatures[this.myIndex] = sig;
1155
- return new MusigSigned(
1156
- this.session,
1157
- [...this.partialSignatures],
1158
- sig,
1159
- this.nonce.public
1160
- );
1255
+ return new MusigSigned(this.session, [...this.partialSignatures], sig, this.nonce.public);
1161
1256
  }
1162
1257
  };
1163
1258
  var MusigSigned = class {
@@ -1171,14 +1266,11 @@ var MusigSigned = class {
1171
1266
  if (this.partialSignatures.some((s) => s === null)) {
1172
1267
  throw new Error("not all partial signatures are set");
1173
1268
  }
1174
- return this.session.partialSigAgg(
1175
- this.partialSignatures
1176
- );
1269
+ return this.session.partialSigAgg(this.partialSignatures);
1177
1270
  }
1178
1271
  };
1179
1272
  var create = (privateKey, publicKeys) => {
1180
- if (publicKeys.length < 2)
1181
- throw new Error("need at least 2 keys to aggregate");
1273
+ if (publicKeys.length < 2) throw new Error("need at least 2 keys to aggregate");
1182
1274
  const keys = [...publicKeys];
1183
1275
  assertPublicKeys(keys);
1184
1276
  Object.freeze(keys);
@@ -1186,14 +1278,7 @@ var create = (privateKey, publicKeys) => {
1186
1278
  const myIndex = findKeyIndex(keys, myPublicKey);
1187
1279
  if (myIndex === -1) throw new Error("our key is not in publicKeys");
1188
1280
  const aggPubkey = aggregateKeys(keys);
1189
- return new MusigKeyAgg(
1190
- privateKey,
1191
- myPublicKey,
1192
- keys,
1193
- myIndex,
1194
- aggPubkey,
1195
- aggPubkey
1196
- );
1281
+ return new MusigKeyAgg(privateKey, myPublicKey, keys, myIndex, aggPubkey, aggPubkey);
1197
1282
  };
1198
1283
 
1199
1284
  // src/utils/boltz-swap-tx.ts
@@ -1269,9 +1354,7 @@ var taprootHashTree = (tree) => {
1269
1354
  };
1270
1355
  var tweakMusig = (musig, tree) => {
1271
1356
  const tweak = taprootHashTree(tree).hash;
1272
- return musig.xonlyTweakAdd(
1273
- import_secp256k12.schnorr.utils.taggedHash("TapTweak", musig.aggPubkey, tweak)
1274
- );
1357
+ return musig.xonlyTweakAdd(import_secp256k12.schnorr.utils.taggedHash("TapTweak", musig.aggPubkey, tweak));
1275
1358
  };
1276
1359
  var toXOnly = (pubKey) => {
1277
1360
  if (pubKey.length === 32) return pubKey;
@@ -1283,9 +1366,7 @@ var toXOnly = (pubKey) => {
1283
1366
  }
1284
1367
  return pubKey.subarray(1, 33);
1285
1368
  }
1286
- throw new Error(
1287
- `Invalid public key length: expected 32 or 33 bytes, got ${pubKey.length}`
1288
- );
1369
+ throw new Error(`Invalid public key length: expected 32 or 33 bytes, got ${pubKey.length}`);
1289
1370
  };
1290
1371
  var p2trScript = (publicKey) => import_btc_signer.Script.encode(["OP_1", toXOnly(publicKey)]);
1291
1372
  var detectSwapOutput = (tweakedKey, transaction) => {
@@ -1300,8 +1381,7 @@ var detectSwapOutput = (tweakedKey, transaction) => {
1300
1381
  };
1301
1382
  var DUMMY_TAPROOT_SIGNATURE = new Uint8Array(64);
1302
1383
  var constructClaimTransaction = (utxo, destinationScript, fee) => {
1303
- if (fee < BigInt(0) || fee >= utxo.amount)
1304
- throw new Error("fee exceeds utxo amount");
1384
+ if (fee < BigInt(0) || fee >= utxo.amount) throw new Error("fee exceeds utxo amount");
1305
1385
  const tx = new import_btc_signer.Transaction({ version: 2 });
1306
1386
  tx.addOutput({
1307
1387
  amount: utxo.amount - fee,
@@ -1320,9 +1400,7 @@ var constructClaimTransaction = (utxo, destinationScript, fee) => {
1320
1400
  };
1321
1401
  var targetFee = (satPerVbyte, constructTx) => {
1322
1402
  const tx = constructTx(BigInt(1));
1323
- return constructTx(
1324
- BigInt(Math.ceil((tx.vsize + tx.inputsLength) * satPerVbyte))
1325
- );
1403
+ return constructTx(BigInt(Math.ceil((tx.vsize + tx.inputsLength) * satPerVbyte)));
1326
1404
  };
1327
1405
 
1328
1406
  // src/utils/decoding.ts
@@ -1330,9 +1408,7 @@ var import_light_bolt11_decoder = __toESM(require("light-bolt11-decoder"), 1);
1330
1408
  var import_sdk2 = require("@arkade-os/sdk");
1331
1409
  var decodeInvoice = (invoice) => {
1332
1410
  const decoded = import_light_bolt11_decoder.default.decode(invoice);
1333
- const millisats = Number(
1334
- decoded.sections.find((s) => s.name === "amount")?.value ?? "0"
1335
- );
1411
+ const millisats = Number(decoded.sections.find((s) => s.name === "amount")?.value ?? "0");
1336
1412
  return {
1337
1413
  expiry: decoded.expiry ?? 3600,
1338
1414
  amountSats: Math.floor(millisats / 1e3),
@@ -1406,10 +1482,7 @@ function extractTimeLockFromLeafOutput(scriptHex) {
1406
1482
  const data = opcodes[hasCSV - 1];
1407
1483
  if (data instanceof Uint8Array) {
1408
1484
  const dataBytes = new Uint8Array(data).reverse();
1409
- const {
1410
- blocks,
1411
- seconds
1412
- } = import_bip68.default.decode(
1485
+ const { blocks, seconds } = import_bip68.default.decode(
1413
1486
  parseInt(import_base5.hex.encode(dataBytes), 16)
1414
1487
  );
1415
1488
  return blocks ?? seconds ?? 0;
@@ -1445,6 +1518,13 @@ var SwapManager = class _SwapManager {
1445
1518
  * enough that a real "swap unknown to this provider" surfaces quickly.
1446
1519
  */
1447
1520
  static NOT_FOUND_THRESHOLD = 10;
1521
+ /**
1522
+ * Delay between re-attempts of a chain refund that left VTXOs deferred
1523
+ * (e.g. pre-CLTV recoverable VTXO, or Boltz 3-of-3 rejected before CLTV
1524
+ * has elapsed). Boltz won't send another status update once the swap
1525
+ * is `swap.expired`, so the manager owns the local retry cadence.
1526
+ */
1527
+ static REFUND_RETRY_DELAY_MS = 6e4;
1448
1528
  swapProvider;
1449
1529
  config;
1450
1530
  // Event listeners storage (supports multiple listeners per event)
@@ -1463,6 +1543,11 @@ var SwapManager = class _SwapManager {
1463
1543
  reconnectTimer = null;
1464
1544
  initialPollTimer = null;
1465
1545
  pollRetryTimers = /* @__PURE__ */ new Map();
1546
+ // Per-swap retry timers for chain refunds that left work undone
1547
+ // (refundArk returned `skipped > 0`). The swap is held in
1548
+ // `monitoredSwaps` past its terminal Boltz status until the local
1549
+ // refund completes or the manager stops.
1550
+ refundRetryTimers = /* @__PURE__ */ new Map();
1466
1551
  // Per-swap counter of consecutive `SwapNotFoundError` responses from
1467
1552
  // `getSwapStatus`. Reset on any successful poll. Once a swap reaches
1468
1553
  // `NOT_FOUND_THRESHOLD` consecutive 404s the safety net trips and the
@@ -1516,9 +1601,7 @@ var SwapManager = class _SwapManager {
1516
1601
  this.wsConnectedListeners.add(config.events.onWebSocketConnected);
1517
1602
  }
1518
1603
  if (config.events?.onWebSocketDisconnected) {
1519
- this.wsDisconnectedListeners.add(
1520
- config.events.onWebSocketDisconnected
1521
- );
1604
+ this.wsDisconnectedListeners.add(config.events.onWebSocketDisconnected);
1522
1605
  }
1523
1606
  this.currentReconnectDelay = this.config.reconnectDelayMs;
1524
1607
  this.currentPollRetryDelay = this.config.pollRetryDelayMs;
@@ -1661,6 +1744,10 @@ var SwapManager = class _SwapManager {
1661
1744
  clearTimeout(timer);
1662
1745
  }
1663
1746
  this.pollRetryTimers.clear();
1747
+ for (const timer of this.refundRetryTimers.values()) {
1748
+ clearTimeout(timer);
1749
+ }
1750
+ this.refundRetryTimers.clear();
1664
1751
  this.notFoundCounts.clear();
1665
1752
  }
1666
1753
  /**
@@ -1669,9 +1756,7 @@ var SwapManager = class _SwapManager {
1669
1756
  */
1670
1757
  setPollInterval(ms) {
1671
1758
  if (ms <= 0) {
1672
- throw new RangeError(
1673
- `setPollInterval: ms must be a positive number, got ${ms}`
1674
- );
1759
+ throw new RangeError(`setPollInterval: ms must be a positive number, got ${ms}`);
1675
1760
  }
1676
1761
  const cappedInterval = Math.min(ms, this.config.maxPollIntervalMs);
1677
1762
  if (cappedInterval !== ms) {
@@ -1680,10 +1765,7 @@ var SwapManager = class _SwapManager {
1680
1765
  );
1681
1766
  }
1682
1767
  this.config.pollInterval = cappedInterval;
1683
- this.currentPollRetryDelay = Math.min(
1684
- cappedInterval,
1685
- this.config.pollRetryDelayMs
1686
- );
1768
+ this.currentPollRetryDelay = Math.min(cappedInterval, this.config.pollRetryDelayMs);
1687
1769
  if (this.isRunning) {
1688
1770
  if (this.usePollingFallback) {
1689
1771
  this.startPollingFallback();
@@ -1722,6 +1804,11 @@ var SwapManager = class _SwapManager {
1722
1804
  clearTimeout(retryTimer);
1723
1805
  this.pollRetryTimers.delete(swapId);
1724
1806
  }
1807
+ const refundRetryTimer = this.refundRetryTimers.get(swapId);
1808
+ if (refundRetryTimer) {
1809
+ clearTimeout(refundRetryTimer);
1810
+ this.refundRetryTimers.delete(swapId);
1811
+ }
1725
1812
  this.notFoundCounts.delete(swapId);
1726
1813
  logger.log(`Removed swap ${swapId} from monitoring`);
1727
1814
  }
@@ -1768,9 +1855,7 @@ var SwapManager = class _SwapManager {
1768
1855
  }
1769
1856
  if (this.isFinalStatus(swap)) {
1770
1857
  if (isPendingReverseSwap(swap)) {
1771
- const response = await this.swapProvider.getReverseSwapTxId(
1772
- swap.id
1773
- );
1858
+ const response = await this.swapProvider.getReverseSwapTxId(swap.id);
1774
1859
  return { txid: response.id };
1775
1860
  }
1776
1861
  if (isPendingSubmarineSwap(swap)) {
@@ -1787,31 +1872,19 @@ var SwapManager = class _SwapManager {
1787
1872
  if (updatedSwap.status === "invoice.settled") {
1788
1873
  this.swapProvider.getReverseSwapTxId(updatedSwap.id).then((response) => resolve({ txid: response.id })).catch((error) => reject(error));
1789
1874
  } else {
1790
- reject(
1791
- new Error(
1792
- `Swap failed with status: ${updatedSwap.status}`
1793
- )
1794
- );
1875
+ reject(new Error(`Swap failed with status: ${updatedSwap.status}`));
1795
1876
  }
1796
1877
  } else if (isPendingSubmarineSwap(updatedSwap)) {
1797
1878
  if (updatedSwap.status === "transaction.claimed") {
1798
1879
  resolve({ txid: updatedSwap.id });
1799
1880
  } else {
1800
- reject(
1801
- new Error(
1802
- `Swap failed with status: ${updatedSwap.status}`
1803
- )
1804
- );
1881
+ reject(new Error(`Swap failed with status: ${updatedSwap.status}`));
1805
1882
  }
1806
1883
  } else if (isPendingChainSwap(updatedSwap)) {
1807
1884
  if (updatedSwap.status === "transaction.claimed") {
1808
1885
  resolve({ txid: updatedSwap.id });
1809
1886
  } else {
1810
- reject(
1811
- new Error(
1812
- `Swap failed with status: ${updatedSwap.status}`
1813
- )
1814
- );
1887
+ reject(new Error(`Swap failed with status: ${updatedSwap.status}`));
1815
1888
  }
1816
1889
  }
1817
1890
  };
@@ -1854,16 +1927,12 @@ var SwapManager = class _SwapManager {
1854
1927
  const connectionTimeout = setTimeout(() => {
1855
1928
  logger.error("WebSocket connection timeout");
1856
1929
  this.websocket?.close();
1857
- this.enterPollingFallback(
1858
- new NetworkError("WebSocket connection failed")
1859
- );
1930
+ this.enterPollingFallback(new NetworkError("WebSocket connection failed"));
1860
1931
  }, 1e4);
1861
1932
  this.websocket.onerror = (error) => {
1862
1933
  clearTimeout(connectionTimeout);
1863
1934
  logger.error("WebSocket error:", error);
1864
- this.enterPollingFallback(
1865
- new NetworkError("WebSocket connection failed")
1866
- );
1935
+ this.enterPollingFallback(new NetworkError("WebSocket connection failed"));
1867
1936
  };
1868
1937
  this.websocket.onopen = () => {
1869
1938
  clearTimeout(connectionTimeout);
@@ -1903,9 +1972,7 @@ var SwapManager = class _SwapManager {
1903
1972
  };
1904
1973
  } catch (error) {
1905
1974
  logger.error("Failed to create WebSocket:", error);
1906
- this.enterPollingFallback(
1907
- new NetworkError("WebSocket connection failed")
1908
- );
1975
+ this.enterPollingFallback(new NetworkError("WebSocket connection failed"));
1909
1976
  }
1910
1977
  }
1911
1978
  /**
@@ -1940,11 +2007,8 @@ var SwapManager = class _SwapManager {
1940
2007
  * Schedule WebSocket reconnection with exponential backoff
1941
2008
  */
1942
2009
  scheduleReconnect() {
1943
- if (this.reconnectTimer || this.webSocketUnavailable || !this.hasWebSocketSupport())
1944
- return;
1945
- logger.log(
1946
- `Scheduling WebSocket reconnect in ${this.currentReconnectDelay}ms`
1947
- );
2010
+ if (this.reconnectTimer || this.webSocketUnavailable || !this.hasWebSocketSupport()) return;
2011
+ logger.log(`Scheduling WebSocket reconnect in ${this.currentReconnectDelay}ms`);
1948
2012
  this.reconnectTimer = setTimeout(() => {
1949
2013
  this.reconnectTimer = null;
1950
2014
  this.isReconnecting = false;
@@ -1959,8 +2023,7 @@ var SwapManager = class _SwapManager {
1959
2023
  * Subscribe to a specific swap ID on the WebSocket
1960
2024
  */
1961
2025
  subscribeToSwap(swapId) {
1962
- if (!this.websocket || this.websocket.readyState !== WebSocket.OPEN)
1963
- return;
2026
+ if (!this.websocket || this.websocket.readyState !== WebSocket.OPEN) return;
1964
2027
  this.websocket.send(
1965
2028
  JSON.stringify({
1966
2029
  op: "subscribe",
@@ -1983,9 +2046,7 @@ var SwapManager = class _SwapManager {
1983
2046
  if (msg.args[0].error) {
1984
2047
  logger.error(`Swap ${swapId} error:`, msg.args[0].error);
1985
2048
  const error = new Error(msg.args[0].error);
1986
- this.swapFailedListeners.forEach(
1987
- (listener) => listener(swap, error)
1988
- );
2049
+ this.swapFailedListeners.forEach((listener) => listener(swap, error));
1989
2050
  return;
1990
2051
  }
1991
2052
  const newStatus = msg.args[0].status;
@@ -2003,19 +2064,14 @@ var SwapManager = class _SwapManager {
2003
2064
  const oldStatus = swap.status;
2004
2065
  if (oldStatus === newStatus) return;
2005
2066
  swap.status = newStatus;
2006
- this.swapUpdateListeners.forEach(
2007
- (listener) => listener(swap, oldStatus)
2008
- );
2067
+ this.swapUpdateListeners.forEach((listener) => listener(swap, oldStatus));
2009
2068
  const subscribers = this.swapSubscriptions.get(swap.id);
2010
2069
  if (subscribers) {
2011
2070
  subscribers.forEach((callback) => {
2012
2071
  try {
2013
2072
  callback(swap, oldStatus);
2014
2073
  } catch (error) {
2015
- logger.error(
2016
- `Error in swap subscription callback for ${swap.id}:`,
2017
- error
2018
- );
2074
+ logger.error(`Error in swap subscription callback for ${swap.id}:`, error);
2019
2075
  }
2020
2076
  });
2021
2077
  }
@@ -2024,15 +2080,57 @@ var SwapManager = class _SwapManager {
2024
2080
  await this.executeAutonomousAction(swap);
2025
2081
  }
2026
2082
  if (this.isFinalStatus(swap)) {
2027
- this.monitoredSwaps.delete(swap.id);
2028
- this.swapSubscriptions.delete(swap.id);
2029
- const retryTimer = this.pollRetryTimers.get(swap.id);
2030
- if (retryTimer) {
2031
- clearTimeout(retryTimer);
2032
- this.pollRetryTimers.delete(swap.id);
2083
+ if (this.refundRetryTimers.has(swap.id)) {
2084
+ return;
2033
2085
  }
2034
- this.swapCompletedListeners.forEach((listener) => listener(swap));
2086
+ this.finalizeMonitoredSwap(swap);
2087
+ }
2088
+ }
2089
+ /**
2090
+ * Drop a swap from monitoring and emit the terminal completion event.
2091
+ * Shared between the on-status-update finalization path and the
2092
+ * refund-retry finalization path (used when a previously-deferred
2093
+ * chain refund has finished its remaining work).
2094
+ */
2095
+ finalizeMonitoredSwap(swap) {
2096
+ if (!this.monitoredSwaps.has(swap.id)) return;
2097
+ this.monitoredSwaps.delete(swap.id);
2098
+ this.swapSubscriptions.delete(swap.id);
2099
+ const retryTimer = this.pollRetryTimers.get(swap.id);
2100
+ if (retryTimer) {
2101
+ clearTimeout(retryTimer);
2102
+ this.pollRetryTimers.delete(swap.id);
2035
2103
  }
2104
+ const refundRetry = this.refundRetryTimers.get(swap.id);
2105
+ if (refundRetry) {
2106
+ clearTimeout(refundRetry);
2107
+ this.refundRetryTimers.delete(swap.id);
2108
+ }
2109
+ this.swapCompletedListeners.forEach((listener) => listener(swap));
2110
+ }
2111
+ /**
2112
+ * Schedule another `executeAutonomousAction` run for a chain swap whose
2113
+ * refund left VTXOs deferred. After the retry completes, if no further
2114
+ * deferral was reported, finalize monitoring cleanup.
2115
+ */
2116
+ scheduleRefundRetry(swap, delayMs) {
2117
+ const existing = this.refundRetryTimers.get(swap.id);
2118
+ if (existing) clearTimeout(existing);
2119
+ this.refundRetryTimers.set(
2120
+ swap.id,
2121
+ setTimeout(async () => {
2122
+ this.refundRetryTimers.delete(swap.id);
2123
+ if (!this.isRunning) return;
2124
+ if (!this.monitoredSwaps.has(swap.id)) return;
2125
+ try {
2126
+ await this.executeAutonomousAction(swap);
2127
+ } finally {
2128
+ if (!this.refundRetryTimers.has(swap.id) && this.isFinalStatus(swap)) {
2129
+ this.finalizeMonitoredSwap(swap);
2130
+ }
2131
+ }
2132
+ }, delayMs)
2133
+ );
2036
2134
  }
2037
2135
  /**
2038
2136
  * Execute autonomous action based on swap status
@@ -2040,9 +2138,7 @@ var SwapManager = class _SwapManager {
2040
2138
  */
2041
2139
  async executeAutonomousAction(swap) {
2042
2140
  if (this.swapsInProgress.has(swap.id)) {
2043
- logger.log(
2044
- `Swap ${swap.id} is already being processed, skipping autonomous action`
2045
- );
2141
+ logger.log(`Swap ${swap.id} is already being processed, skipping autonomous action`);
2046
2142
  return;
2047
2143
  }
2048
2144
  try {
@@ -2057,9 +2153,7 @@ var SwapManager = class _SwapManager {
2057
2153
  if (isReverseClaimableStatus(swap.status)) {
2058
2154
  logger.log(`Auto-claiming reverse swap ${swap.id}`);
2059
2155
  await this.executeClaimAction(swap);
2060
- this.actionExecutedListeners.forEach(
2061
- (listener) => listener(swap, "claim")
2062
- );
2156
+ this.actionExecutedListeners.forEach((listener) => listener(swap, "claim"));
2063
2157
  }
2064
2158
  } else if (isPendingSubmarineSwap(swap)) {
2065
2159
  if (!swap.request?.invoice || swap.request.invoice.length === 0) {
@@ -2071,9 +2165,7 @@ var SwapManager = class _SwapManager {
2071
2165
  if (isSubmarineRefundableStatus(swap.status)) {
2072
2166
  logger.log(`Auto-refunding submarine swap ${swap.id}`);
2073
2167
  await this.executeRefundAction(swap);
2074
- this.actionExecutedListeners.forEach(
2075
- (listener) => listener(swap, "refund")
2076
- );
2168
+ this.actionExecutedListeners.forEach((listener) => listener(swap, "refund"));
2077
2169
  }
2078
2170
  } else if (isPendingChainSwap(swap)) {
2079
2171
  if (isChainClaimableStatus(swap.status)) {
@@ -2093,10 +2185,27 @@ var SwapManager = class _SwapManager {
2093
2185
  } else if (isChainRefundableStatus(swap.status)) {
2094
2186
  if (swap.request.from === "ARK") {
2095
2187
  logger.log(`Auto-refunding ARK chain swap ${swap.id}`);
2096
- await this.executeRefundArkAction(swap);
2097
- this.actionExecutedListeners.forEach(
2098
- (listener) => listener(swap, "refundArk")
2099
- );
2188
+ try {
2189
+ const outcome = await this.executeRefundArkAction(swap);
2190
+ if (outcome && outcome.skipped > 0) {
2191
+ logger.log(
2192
+ `Chain swap ${swap.id}: ${outcome.skipped} VTXO(s) deferred \u2014 scheduling refund retry`
2193
+ );
2194
+ this.scheduleRefundRetry(swap, _SwapManager.REFUND_RETRY_DELAY_MS);
2195
+ }
2196
+ this.actionExecutedListeners.forEach(
2197
+ (listener) => listener(swap, "refundArk")
2198
+ );
2199
+ } catch (error) {
2200
+ logger.error(
2201
+ `Auto-refunding ARK chain swap ${swap.id} failed; scheduling retry`,
2202
+ error
2203
+ );
2204
+ this.swapFailedListeners.forEach(
2205
+ (listener) => listener(swap, error)
2206
+ );
2207
+ this.scheduleRefundRetry(swap, _SwapManager.REFUND_RETRY_DELAY_MS);
2208
+ }
2100
2209
  }
2101
2210
  if (swap.request.from === "BTC") {
2102
2211
  logger.warn(
@@ -2123,13 +2232,8 @@ var SwapManager = class _SwapManager {
2123
2232
  }
2124
2233
  }
2125
2234
  } catch (error) {
2126
- logger.error(
2127
- `Failed to execute autonomous action for swap ${swap.id}:`,
2128
- error
2129
- );
2130
- this.swapFailedListeners.forEach(
2131
- (listener) => listener(swap, error)
2132
- );
2235
+ logger.error(`Failed to execute autonomous action for swap ${swap.id}:`, error);
2236
+ this.swapFailedListeners.forEach((listener) => listener(swap, error));
2133
2237
  } finally {
2134
2238
  this.swapsInProgress.delete(swap.id);
2135
2239
  }
@@ -2182,7 +2286,7 @@ var SwapManager = class _SwapManager {
2182
2286
  logger.error("refundArk callback not set");
2183
2287
  return;
2184
2288
  }
2185
- await this.refundArkCallback(swap);
2289
+ return this.refundArkCallback(swap);
2186
2290
  }
2187
2291
  /**
2188
2292
  * Execute sign server claim action for chain swap.
@@ -2230,9 +2334,7 @@ var SwapManager = class _SwapManager {
2230
2334
  logger.log(`Resuming chain refund for swap ${swap.id}`);
2231
2335
  await this.executeAutonomousAction(swap);
2232
2336
  } else if (isPendingChainSwap(swap) && swap.request.to === "ARK" && isChainSignableStatus(swap.status)) {
2233
- logger.log(
2234
- `Resuming server claim signing for swap ${swap.id}`
2235
- );
2337
+ logger.log(`Resuming server claim signing for swap ${swap.id}`);
2236
2338
  await this.executeAutonomousAction(swap);
2237
2339
  }
2238
2340
  } catch (error) {
@@ -2284,16 +2386,12 @@ var SwapManager = class _SwapManager {
2284
2386
  */
2285
2387
  async pollAllSwaps() {
2286
2388
  if (this.monitoredSwaps.size === 0) return;
2287
- const pollPromises = Array.from(this.monitoredSwaps.values()).map(
2288
- (swap) => this.pollSingleSwap(swap)
2289
- );
2389
+ const pollPromises = Array.from(this.monitoredSwaps.values()).filter((swap) => !this.refundRetryTimers.has(swap.id)).map((swap) => this.pollSingleSwap(swap));
2290
2390
  await Promise.allSettled(pollPromises);
2291
2391
  }
2292
2392
  async pollSingleSwap(swap) {
2293
2393
  try {
2294
- const statusResponse = await this.swapProvider.getSwapStatus(
2295
- swap.id
2296
- );
2394
+ const statusResponse = await this.swapProvider.getSwapStatus(swap.id);
2297
2395
  this.notFoundCounts.delete(swap.id);
2298
2396
  if (statusResponse.status !== swap.status) {
2299
2397
  await this.handleSwapStatusUpdate(swap, statusResponse.status);
@@ -2304,9 +2402,7 @@ var SwapManager = class _SwapManager {
2304
2402
  return;
2305
2403
  }
2306
2404
  if (error instanceof NetworkError && error.statusCode === 429) {
2307
- logger.warn(
2308
- `Rate-limited polling swap ${swap.id}, retrying in 2s`
2309
- );
2405
+ logger.warn(`Rate-limited polling swap ${swap.id}, retrying in 2s`);
2310
2406
  const existing = this.pollRetryTimers.get(swap.id);
2311
2407
  if (existing) clearTimeout(existing);
2312
2408
  this.pollRetryTimers.set(
@@ -2314,25 +2410,17 @@ var SwapManager = class _SwapManager {
2314
2410
  setTimeout(async () => {
2315
2411
  this.pollRetryTimers.delete(swap.id);
2316
2412
  try {
2317
- const retry = await this.swapProvider.getSwapStatus(
2318
- swap.id
2319
- );
2413
+ const retry = await this.swapProvider.getSwapStatus(swap.id);
2320
2414
  this.notFoundCounts.delete(swap.id);
2321
2415
  if (retry.status !== swap.status) {
2322
- await this.handleSwapStatusUpdate(
2323
- swap,
2324
- retry.status
2325
- );
2416
+ await this.handleSwapStatusUpdate(swap, retry.status);
2326
2417
  }
2327
2418
  } catch (retryError) {
2328
2419
  if (retryError instanceof SwapNotFoundError) {
2329
2420
  await this.handleSwapNotFound(swap);
2330
2421
  return;
2331
2422
  }
2332
- logger.error(
2333
- `Retry poll for swap ${swap.id} also failed:`,
2334
- retryError
2335
- );
2423
+ logger.error(`Retry poll for swap ${swap.id} also failed:`, retryError);
2336
2424
  }
2337
2425
  }, 2e3)
2338
2426
  );
@@ -2351,6 +2439,7 @@ var SwapManager = class _SwapManager {
2351
2439
  * Boltz endpoint).
2352
2440
  */
2353
2441
  async handleSwapNotFound(swap) {
2442
+ if (this.refundRetryTimers.has(swap.id)) return;
2354
2443
  const count = (this.notFoundCounts.get(swap.id) ?? 0) + 1;
2355
2444
  this.notFoundCounts.set(swap.id, count);
2356
2445
  logger.warn(
@@ -2371,6 +2460,7 @@ var SwapManager = class _SwapManager {
2371
2460
  * 404s without recovering anything.
2372
2461
  */
2373
2462
  async markSwapAsUnknownToProvider(swap) {
2463
+ if (this.refundRetryTimers.has(swap.id)) return;
2374
2464
  if (!this.monitoredSwaps.has(swap.id)) {
2375
2465
  this.notFoundCounts.delete(swap.id);
2376
2466
  return;
@@ -2383,10 +2473,13 @@ var SwapManager = class _SwapManager {
2383
2473
  clearTimeout(retryTimer);
2384
2474
  this.pollRetryTimers.delete(swap.id);
2385
2475
  }
2476
+ const refundRetryTimer = this.refundRetryTimers.get(swap.id);
2477
+ if (refundRetryTimer) {
2478
+ clearTimeout(refundRetryTimer);
2479
+ this.refundRetryTimers.delete(swap.id);
2480
+ }
2386
2481
  this.notFoundCounts.delete(swap.id);
2387
- this.swapUpdateListeners.forEach(
2388
- (listener) => listener(swap, oldStatus)
2389
- );
2482
+ this.swapUpdateListeners.forEach((listener) => listener(swap, oldStatus));
2390
2483
  const subscribers = this.swapSubscriptions.get(swap.id);
2391
2484
  if (subscribers) {
2392
2485
  subscribers.forEach((callback) => {
@@ -2451,9 +2544,7 @@ async function saveSwap(swap, saver) {
2451
2544
  if (saver.saveSubmarineSwap) {
2452
2545
  await saver.saveSubmarineSwap(swap);
2453
2546
  } else {
2454
- console.warn(
2455
- "No saveSubmarineSwap handler provided, swap not saved"
2456
- );
2547
+ console.warn("No saveSubmarineSwap handler provided, swap not saved");
2457
2548
  }
2458
2549
  } else if (isPendingChainSwap(swap)) {
2459
2550
  if (saver.saveChainSwap) {
@@ -2517,6 +2608,26 @@ function enrichSubmarineSwapInvoice(swap, invoice) {
2517
2608
  return swap;
2518
2609
  }
2519
2610
 
2611
+ // src/repositories/swap-repository.ts
2612
+ function hasImpossibleSwapsFilter(filter) {
2613
+ if (!filter) return false;
2614
+ return Array.isArray(filter.id) && filter.id.length === 0 || Array.isArray(filter.status) && filter.status.length === 0 || Array.isArray(filter.type) && filter.type.length === 0;
2615
+ }
2616
+ function matchesCriterion(value, criterion) {
2617
+ if (criterion === void 0) return true;
2618
+ return Array.isArray(criterion) ? criterion.includes(value) : value === criterion;
2619
+ }
2620
+ function applySwapsFilter(swaps, filter) {
2621
+ return swaps.filter(
2622
+ (swap) => !!swap && matchesCriterion(swap.id, filter.id) && matchesCriterion(swap.status, filter.status) && matchesCriterion(swap.type, filter.type)
2623
+ );
2624
+ }
2625
+ function applyCreatedAtOrder(swaps, filter) {
2626
+ if (filter?.orderBy !== "createdAt") return swaps;
2627
+ const direction = filter.orderDirection === "asc" ? 1 : -1;
2628
+ return swaps.slice().sort((a, b) => (a.createdAt - b.createdAt) * direction);
2629
+ }
2630
+
2520
2631
  // src/repositories/IndexedDb/swap-repository.ts
2521
2632
  var import_sdk4 = require("@arkade-os/sdk");
2522
2633
  var DEFAULT_DB_NAME = "arkade-boltz-swap";
@@ -2532,6 +2643,10 @@ function initDatabase(db) {
2532
2643
  swapStore.createIndex("createdAt", "createdAt", { unique: false });
2533
2644
  }
2534
2645
  }
2646
+ function asArray(v) {
2647
+ if (v === void 0) return void 0;
2648
+ return Array.isArray(v) ? v : [v];
2649
+ }
2535
2650
  var IndexedDbSwapRepository = class {
2536
2651
  constructor(dbName = DEFAULT_DB_NAME) {
2537
2652
  this.dbName = dbName;
@@ -2546,10 +2661,7 @@ var IndexedDbSwapRepository = class {
2546
2661
  async saveSwap(swap) {
2547
2662
  const db = await this.getDB();
2548
2663
  return new Promise((resolve, reject) => {
2549
- const transaction = db.transaction(
2550
- [STORE_SWAPS_STATE],
2551
- "readwrite"
2552
- );
2664
+ const transaction = db.transaction([STORE_SWAPS_STATE], "readwrite");
2553
2665
  const store = transaction.objectStore(STORE_SWAPS_STATE);
2554
2666
  const request = store.put(swap);
2555
2667
  request.onsuccess = () => resolve();
@@ -2559,10 +2671,7 @@ var IndexedDbSwapRepository = class {
2559
2671
  async deleteSwap(id) {
2560
2672
  const db = await this.getDB();
2561
2673
  return new Promise((resolve, reject) => {
2562
- const transaction = db.transaction(
2563
- [STORE_SWAPS_STATE],
2564
- "readwrite"
2565
- );
2674
+ const transaction = db.transaction([STORE_SWAPS_STATE], "readwrite");
2566
2675
  const store = transaction.objectStore(STORE_SWAPS_STATE);
2567
2676
  const request = store.delete(id);
2568
2677
  request.onsuccess = () => resolve();
@@ -2575,10 +2684,7 @@ var IndexedDbSwapRepository = class {
2575
2684
  async clear() {
2576
2685
  const db = await this.getDB();
2577
2686
  return new Promise((resolve, reject) => {
2578
- const transaction = db.transaction(
2579
- [STORE_SWAPS_STATE],
2580
- "readwrite"
2581
- );
2687
+ const transaction = db.transaction([STORE_SWAPS_STATE], "readwrite");
2582
2688
  const store = transaction.objectStore(STORE_SWAPS_STATE);
2583
2689
  const request = store.clear();
2584
2690
  request.onsuccess = () => resolve();
@@ -2595,11 +2701,10 @@ var IndexedDbSwapRepository = class {
2595
2701
  request.onsuccess = () => resolve(request.result ?? []);
2596
2702
  })
2597
2703
  );
2598
- return Promise.all(requests).then(
2599
- (results) => results.flatMap((result) => result)
2600
- );
2704
+ return Promise.all(requests).then((results) => results.flatMap((result) => result));
2601
2705
  }
2602
2706
  async getAllSwapsFromStore(filter) {
2707
+ if (hasImpossibleSwapsFilter(filter)) return [];
2603
2708
  const db = await this.getDB();
2604
2709
  const store = db.transaction([STORE_SWAPS_STATE], "readonly").objectStore(STORE_SWAPS_STATE);
2605
2710
  if (!filter || Object.keys(filter).length === 0) {
@@ -2609,9 +2714,8 @@ var IndexedDbSwapRepository = class {
2609
2714
  request.onerror = () => reject(request.error);
2610
2715
  });
2611
2716
  }
2612
- const normalizedFilter = normalizeFilter(filter);
2613
- if (normalizedFilter.has("id")) {
2614
- const ids = normalizedFilter.get("id");
2717
+ const ids = asArray(filter.id);
2718
+ if (ids) {
2615
2719
  const swaps = await Promise.all(
2616
2720
  ids.map(
2617
2721
  (id) => new Promise((resolve, reject) => {
@@ -2621,34 +2725,17 @@ var IndexedDbSwapRepository = class {
2621
2725
  })
2622
2726
  )
2623
2727
  );
2624
- return this.sortIfNeeded(
2625
- this.applySwapsFilter(swaps, normalizedFilter),
2626
- filter
2627
- );
2728
+ return applyCreatedAtOrder(applySwapsFilter(swaps, filter), filter);
2628
2729
  }
2629
- if (normalizedFilter.has("type")) {
2630
- const types = normalizedFilter.get("type");
2631
- const swaps = await this.getSwapsByIndexValues(
2632
- store,
2633
- "type",
2634
- types
2635
- );
2636
- return this.sortIfNeeded(
2637
- this.applySwapsFilter(swaps, normalizedFilter),
2638
- filter
2639
- );
2730
+ const types = asArray(filter.type);
2731
+ if (types) {
2732
+ const swaps = await this.getSwapsByIndexValues(store, "type", types);
2733
+ return applyCreatedAtOrder(applySwapsFilter(swaps, filter), filter);
2640
2734
  }
2641
- if (normalizedFilter.has("status")) {
2642
- const ids = normalizedFilter.get("status");
2643
- const swaps = await this.getSwapsByIndexValues(
2644
- store,
2645
- "status",
2646
- ids
2647
- );
2648
- return this.sortIfNeeded(
2649
- this.applySwapsFilter(swaps, normalizedFilter),
2650
- filter
2651
- );
2735
+ const statuses = asArray(filter.status);
2736
+ if (statuses) {
2737
+ const swaps = await this.getSwapsByIndexValues(store, "status", statuses);
2738
+ return applyCreatedAtOrder(applySwapsFilter(swaps, filter), filter);
2652
2739
  }
2653
2740
  if (filter.orderBy === "createdAt") {
2654
2741
  return this.getAllSwapsByCreatedAt(store, filter.orderDirection);
@@ -2658,22 +2745,7 @@ var IndexedDbSwapRepository = class {
2658
2745
  request.onsuccess = () => resolve(request.result ?? []);
2659
2746
  request.onerror = () => reject(request.error);
2660
2747
  });
2661
- return this.sortIfNeeded(
2662
- this.applySwapsFilter(allSwaps, normalizedFilter),
2663
- filter
2664
- );
2665
- }
2666
- applySwapsFilter(swaps, filter) {
2667
- return swaps.filter((swap) => {
2668
- if (swap === void 0) return false;
2669
- if (filter.has("id") && !filter.get("id")?.includes(swap.id))
2670
- return false;
2671
- if (filter.has("status") && !filter.get("status")?.includes(swap.status))
2672
- return false;
2673
- if (filter.has("type") && !filter.get("type")?.includes(swap.type))
2674
- return false;
2675
- return true;
2676
- });
2748
+ return applyCreatedAtOrder(applySwapsFilter(allSwaps, filter), filter);
2677
2749
  }
2678
2750
  async getAllSwapsByCreatedAt(store, orderDirection) {
2679
2751
  const index = store.index("createdAt");
@@ -2693,30 +2765,12 @@ var IndexedDbSwapRepository = class {
2693
2765
  };
2694
2766
  });
2695
2767
  }
2696
- sortIfNeeded(swaps, filter) {
2697
- if (filter?.orderBy !== "createdAt") return swaps;
2698
- const direction = filter.orderDirection === "asc" ? 1 : -1;
2699
- return swaps.slice().sort((a, b) => (a.createdAt - b.createdAt) * direction);
2700
- }
2701
2768
  async [Symbol.asyncDispose]() {
2702
2769
  if (!this.db) return;
2703
2770
  await (0, import_sdk4.closeDatabase)(this.dbName);
2704
2771
  this.db = null;
2705
2772
  }
2706
2773
  };
2707
- var FILTER_FIELDS = ["id", "status", "type"];
2708
- function normalizeFilter(filter) {
2709
- const res = /* @__PURE__ */ new Map();
2710
- FILTER_FIELDS.forEach((current) => {
2711
- if (!filter?.[current]) return;
2712
- if (Array.isArray(filter[current])) {
2713
- res.set(current, filter[current]);
2714
- } else {
2715
- res.set(current, [filter[current]]);
2716
- }
2717
- });
2718
- return res;
2719
- }
2720
2774
 
2721
2775
  // src/utils/identity.ts
2722
2776
  var import_sdk5 = require("@arkade-os/sdk");
@@ -2728,13 +2782,8 @@ function claimVHTLCIdentity(identity, preimage) {
2728
2782
  let signedTx = await identity.sign(cpy, inputIndexes);
2729
2783
  signedTx = import_sdk5.Transaction.fromPSBT(signedTx.toPSBT());
2730
2784
  if (preimage) {
2731
- for (const inputIndex of inputIndexes || Array.from(
2732
- { length: signedTx.inputsLength },
2733
- (_, i) => i
2734
- )) {
2735
- (0, import_sdk5.setArkPsbtField)(signedTx, inputIndex, import_sdk5.ConditionWitness, [
2736
- preimage
2737
- ]);
2785
+ for (const inputIndex of inputIndexes || Array.from({ length: signedTx.inputsLength }, (_, i) => i)) {
2786
+ (0, import_sdk5.setArkPsbtField)(signedTx, inputIndex, import_sdk5.ConditionWitness, [preimage]);
2738
2787
  }
2739
2788
  }
2740
2789
  return signedTx;
@@ -2789,17 +2838,13 @@ function createVHTLCBatchHandler(intentId, vhtlc, arkProvider, identity, session
2789
2838
  if (!sweepTapTreeRoot) {
2790
2839
  throw new Error("Sweep tap tree root not set");
2791
2840
  }
2792
- const xOnlyPublicKeys = event.cosignersPublicKeys.map(
2793
- (k) => k.slice(2)
2794
- );
2841
+ const xOnlyPublicKeys = event.cosignersPublicKeys.map((k) => k.slice(2));
2795
2842
  const signerPublicKey = await session.getPublicKey();
2796
2843
  const xonlySignerPublicKey = signerPublicKey.subarray(1);
2797
2844
  if (!xOnlyPublicKeys.includes(import_base7.hex.encode(xonlySignerPublicKey))) {
2798
2845
  return { skip: true };
2799
2846
  }
2800
- const commitmentTx = import_sdk6.Transaction.fromPSBT(
2801
- import_base7.base64.decode(event.unsignedCommitmentTx)
2802
- );
2847
+ const commitmentTx = import_sdk6.Transaction.fromPSBT(import_base7.base64.decode(event.unsignedCommitmentTx));
2803
2848
  (0, import_sdk6.validateVtxoTxGraph)(vtxoTree, commitmentTx, sweepTapTreeRoot);
2804
2849
  const sharedOutput = commitmentTx.getOutput(0);
2805
2850
  if (!sharedOutput?.amount) {
@@ -2815,18 +2860,11 @@ function createVHTLCBatchHandler(intentId, vhtlc, arkProvider, identity, session
2815
2860
  if (!session) {
2816
2861
  return { fullySigned: true };
2817
2862
  }
2818
- const { hasAllNonces } = await session.aggregatedNonces(
2819
- event.txid,
2820
- event.nonces
2821
- );
2863
+ const { hasAllNonces } = await session.aggregatedNonces(event.txid, event.nonces);
2822
2864
  if (!hasAllNonces) return { fullySigned: false };
2823
2865
  const signatures = await session.sign();
2824
2866
  const pubkey = import_base7.hex.encode(await session.getPublicKey());
2825
- await arkProvider.submitTreeSignatures(
2826
- event.id,
2827
- pubkey,
2828
- signatures
2829
- );
2867
+ await arkProvider.submitTreeSignatures(event.id, pubkey, signatures);
2830
2868
  return { fullySigned: true };
2831
2869
  },
2832
2870
  onBatchFinalization: async (event, _, connectorTree) => {
@@ -2834,9 +2872,7 @@ function createVHTLCBatchHandler(intentId, vhtlc, arkProvider, identity, session
2834
2872
  return;
2835
2873
  }
2836
2874
  if (!connectorTree) {
2837
- throw new Error(
2838
- "BatchFinalizationEvent: expected connector tree to be defined"
2839
- );
2875
+ throw new Error("BatchFinalizationEvent: expected connector tree to be defined");
2840
2876
  }
2841
2877
  (0, import_sdk6.validateConnectorsTxGraph)(event.commitmentTx, connectorTree);
2842
2878
  const connectors = connectorTree.leaves();
@@ -2851,9 +2887,7 @@ function createVHTLCBatchHandler(intentId, vhtlc, arkProvider, identity, session
2851
2887
  connectors[connectorIndex]
2852
2888
  );
2853
2889
  const signedForfeitTx = await identity.sign(forfeitTx);
2854
- await arkProvider.submitSignedForfeitTxs([
2855
- import_base7.base64.encode(signedForfeitTx.toPSBT())
2856
- ]);
2890
+ await arkProvider.submitSignedForfeitTxs([import_base7.base64.encode(signedForfeitTx.toPSBT())]);
2857
2891
  }
2858
2892
  };
2859
2893
  }
@@ -2913,36 +2947,22 @@ var createVHTLCScript = (args) => {
2913
2947
  serverPubkey,
2914
2948
  timeoutBlockHeights: vhtlcTimeouts
2915
2949
  } = args;
2916
- const receiverXOnlyPublicKey = normalizeToXOnlyKey(
2917
- import_base8.hex.decode(receiverPubkey),
2918
- "receiver"
2919
- );
2920
- const senderXOnlyPublicKey = normalizeToXOnlyKey(
2921
- import_base8.hex.decode(senderPubkey),
2922
- "sender"
2923
- );
2924
- const serverXOnlyPublicKey = normalizeToXOnlyKey(
2925
- import_base8.hex.decode(serverPubkey),
2926
- "server"
2927
- );
2950
+ const receiverXOnlyPublicKey = normalizeToXOnlyKey(import_base8.hex.decode(receiverPubkey), "receiver");
2951
+ const senderXOnlyPublicKey = normalizeToXOnlyKey(import_base8.hex.decode(senderPubkey), "sender");
2952
+ const serverXOnlyPublicKey = normalizeToXOnlyKey(import_base8.hex.decode(serverPubkey), "server");
2928
2953
  const vhtlcScript = new import_sdk7.VHTLC.Script({
2929
2954
  preimageHash: (0, import_legacy.ripemd160)(preimageHash),
2930
2955
  sender: senderXOnlyPublicKey,
2931
2956
  receiver: receiverXOnlyPublicKey,
2932
2957
  server: serverXOnlyPublicKey,
2933
2958
  refundLocktime: BigInt(vhtlcTimeouts.refund),
2934
- unilateralClaimDelay: toBip68RelativeTimelock(
2935
- vhtlcTimeouts.unilateralClaim
2936
- ),
2937
- unilateralRefundDelay: toBip68RelativeTimelock(
2938
- vhtlcTimeouts.unilateralRefund
2939
- ),
2959
+ unilateralClaimDelay: toBip68RelativeTimelock(vhtlcTimeouts.unilateralClaim),
2960
+ unilateralRefundDelay: toBip68RelativeTimelock(vhtlcTimeouts.unilateralRefund),
2940
2961
  unilateralRefundWithoutReceiverDelay: toBip68RelativeTimelock(
2941
2962
  vhtlcTimeouts.unilateralRefundWithoutReceiver
2942
2963
  )
2943
2964
  });
2944
- if (!vhtlcScript.claimScript)
2945
- throw new Error("Failed to create VHTLC script");
2965
+ if (!vhtlcScript.claimScript) throw new Error("Failed to create VHTLC script");
2946
2966
  const hrp = network === "bitcoin" ? "ark" : "tark";
2947
2967
  const vhtlcAddress = vhtlcScript.address(hrp, serverXOnlyPublicKey).encode();
2948
2968
  return { vhtlcScript, vhtlcAddress };
@@ -2976,11 +2996,7 @@ var joinBatch = async (arkProvider, identity, input, output, {
2976
2996
  unknown: [import_sdk7.VtxoTaprootTree.encode(input.tapTree)],
2977
2997
  sequence: (0, import_sdk7.getSequence)(input.tapLeafScript)
2978
2998
  };
2979
- const registerIntent = import_sdk7.Intent.create(
2980
- intentMessage,
2981
- [intentInput],
2982
- [output]
2983
- );
2999
+ const registerIntent = import_sdk7.Intent.create(intentMessage, [intentInput], [output]);
2984
3000
  const deleteIntent = import_sdk7.Intent.create(deleteMessage, [intentInput]);
2985
3001
  const [signedRegisterIntent, signedDeleteIntent] = await Promise.all([
2986
3002
  identity.sign(registerIntent),
@@ -3004,14 +3020,8 @@ var joinBatch = async (arkProvider, identity, input, output, {
3004
3020
  normalizeToXOnlyKey(forfeitPubkey, "forfeit"),
3005
3021
  isRecoverable2 ? void 0 : import_btc_signer4.OutScript.encode(decodedAddress)
3006
3022
  );
3007
- const topics = [
3008
- import_base8.hex.encode(signerPublicKey),
3009
- `${input.txid}:${input.vout}`
3010
- ];
3011
- const eventStream = arkProvider.getEventStream(
3012
- abortController.signal,
3013
- topics
3014
- );
3023
+ const topics = [import_base8.hex.encode(signerPublicKey), `${input.txid}:${input.vout}`];
3024
+ const eventStream = arkProvider.getEventStream(abortController.signal, topics);
3015
3025
  const commitmentTxid = await import_sdk7.Batch.join(eventStream, handler, {
3016
3026
  abortController
3017
3027
  });
@@ -3032,14 +3042,8 @@ var joinBatch = async (arkProvider, identity, input, output, {
3032
3042
  };
3033
3043
  var claimVHTLCwithOffchainTx = async (identity, vhtlcScript, serverXOnlyPublicKey, input, output, arkInfo, arkProvider) => {
3034
3044
  const rawCheckpointTapscript = import_base8.hex.decode(arkInfo.checkpointTapscript);
3035
- const serverUnrollScript = import_sdk7.CSVMultisigTapscript.decode(
3036
- rawCheckpointTapscript
3037
- );
3038
- const { arkTx, checkpoints } = (0, import_sdk7.buildOffchainTx)(
3039
- [input],
3040
- [output],
3041
- serverUnrollScript
3042
- );
3045
+ const serverUnrollScript = import_sdk7.CSVMultisigTapscript.decode(rawCheckpointTapscript);
3046
+ const { arkTx, checkpoints } = (0, import_sdk7.buildOffchainTx)([input], [output], serverUnrollScript);
3043
3047
  const signedArkTx = await identity.sign(arkTx);
3044
3048
  const { arkTxid, finalArkTx, signedCheckpointTxs } = await arkProvider.submitTx(
3045
3049
  import_base8.base64.encode(signedArkTx.toPSBT()),
@@ -3047,9 +3051,7 @@ var claimVHTLCwithOffchainTx = async (identity, vhtlcScript, serverXOnlyPublicKe
3047
3051
  );
3048
3052
  const finalTx = import_sdk7.Transaction.fromPSBT(import_base8.base64.decode(finalArkTx));
3049
3053
  const serverPubkeyHex = import_base8.hex.encode(serverXOnlyPublicKey);
3050
- const claimLeafHash = (0, import_payment3.tapLeafHash)(
3051
- scriptFromTapLeafScript(vhtlcScript.claim())
3052
- );
3054
+ const claimLeafHash = (0, import_payment3.tapLeafHash)(scriptFromTapLeafScript(vhtlcScript.claim()));
3053
3055
  for (let i = 0; i < finalTx.inputsLength; i++) {
3054
3056
  if (!verifySignatures(finalTx, i, [serverPubkeyHex], claimLeafHash)) {
3055
3057
  throw new Error("Invalid final Ark transaction");
@@ -3059,13 +3061,9 @@ var claimVHTLCwithOffchainTx = async (identity, vhtlcScript, serverXOnlyPublicKe
3059
3061
  signedCheckpointTxs.map(async (c, idx) => {
3060
3062
  const tx = import_sdk7.Transaction.fromPSBT(import_base8.base64.decode(c));
3061
3063
  const checkpointLeaf = checkpoints[idx].getInput(0).tapLeafScript[0];
3062
- const cpLeafHash = (0, import_payment3.tapLeafHash)(
3063
- scriptFromTapLeafScript(checkpointLeaf)
3064
- );
3064
+ const cpLeafHash = (0, import_payment3.tapLeafHash)(scriptFromTapLeafScript(checkpointLeaf));
3065
3065
  if (!verifySignatures(tx, 0, [serverPubkeyHex], cpLeafHash)) {
3066
- throw new Error(
3067
- "Invalid server signature in checkpoint transaction"
3068
- );
3066
+ throw new Error("Invalid server signature in checkpoint transaction");
3069
3067
  }
3070
3068
  const signedCheckpoint = await identity.sign(tx, [0]);
3071
3069
  return import_base8.base64.encode(signedCheckpoint.toPSBT());
@@ -3075,61 +3073,37 @@ var claimVHTLCwithOffchainTx = async (identity, vhtlcScript, serverXOnlyPublicKe
3075
3073
  };
3076
3074
  var refundVHTLCwithOffchainTx = async (swapId, identity, arkProvider, boltzXOnlyPublicKey, ourXOnlyPublicKey, serverXOnlyPublicKey, input, output, arkInfo, refundFunc) => {
3077
3075
  const rawCheckpointTapscript = import_base8.hex.decode(arkInfo.checkpointTapscript);
3078
- const serverUnrollScript = import_sdk7.CSVMultisigTapscript.decode(
3079
- rawCheckpointTapscript
3076
+ const serverUnrollScript = import_sdk7.CSVMultisigTapscript.decode(rawCheckpointTapscript);
3077
+ const { arkTx: unsignedRefundTx, checkpoints: checkpointPtxs } = (0, import_sdk7.buildOffchainTx)(
3078
+ [input],
3079
+ [output],
3080
+ serverUnrollScript
3080
3081
  );
3081
- const { arkTx: unsignedRefundTx, checkpoints: checkpointPtxs } = (0, import_sdk7.buildOffchainTx)([input], [output], serverUnrollScript);
3082
3082
  if (checkpointPtxs.length !== 1)
3083
- throw new Error(
3084
- `Expected one checkpoint transaction, got ${checkpointPtxs.length}`
3085
- );
3083
+ throw new Error(`Expected one checkpoint transaction, got ${checkpointPtxs.length}`);
3086
3084
  const unsignedCheckpointTx = checkpointPtxs[0];
3087
3085
  let boltzSignedRefundTx;
3088
3086
  let boltzSignedCheckpointTx;
3089
3087
  try {
3090
- const result = await refundFunc(
3091
- swapId,
3092
- unsignedRefundTx,
3093
- unsignedCheckpointTx
3094
- );
3088
+ const result = await refundFunc(swapId, unsignedRefundTx, unsignedCheckpointTx);
3095
3089
  boltzSignedRefundTx = result.transaction;
3096
3090
  boltzSignedCheckpointTx = result.checkpoint;
3097
3091
  } catch (error) {
3098
- throw new BoltzRefundError(
3099
- `Boltz rejected refund for swap ${swapId}`,
3100
- error
3101
- );
3092
+ throw new BoltzRefundError(`Boltz rejected refund for swap ${swapId}`, error);
3102
3093
  }
3103
3094
  const boltzXOnlyPublicKeyHex = import_base8.hex.encode(boltzXOnlyPublicKey);
3104
- const refundLeafHash = (0, import_payment3.tapLeafHash)(
3105
- scriptFromTapLeafScript(input.tapLeafScript)
3106
- );
3107
- if (!verifySignatures(
3108
- boltzSignedRefundTx,
3109
- 0,
3110
- [boltzXOnlyPublicKeyHex],
3111
- refundLeafHash
3112
- )) {
3095
+ const refundLeafHash = (0, import_payment3.tapLeafHash)(scriptFromTapLeafScript(input.tapLeafScript));
3096
+ if (!verifySignatures(boltzSignedRefundTx, 0, [boltzXOnlyPublicKeyHex], refundLeafHash)) {
3113
3097
  throw new Error("Invalid Boltz signature in refund transaction");
3114
3098
  }
3115
3099
  const checkpointLeaf = unsignedCheckpointTx.getInput(0).tapLeafScript[0];
3116
- const checkpointLeafHash = (0, import_payment3.tapLeafHash)(
3117
- scriptFromTapLeafScript(checkpointLeaf)
3118
- );
3119
- if (!verifySignatures(
3120
- boltzSignedCheckpointTx,
3121
- 0,
3122
- [boltzXOnlyPublicKeyHex],
3123
- checkpointLeafHash
3124
- )) {
3100
+ const checkpointLeafHash = (0, import_payment3.tapLeafHash)(scriptFromTapLeafScript(checkpointLeaf));
3101
+ if (!verifySignatures(boltzSignedCheckpointTx, 0, [boltzXOnlyPublicKeyHex], checkpointLeafHash)) {
3125
3102
  throw new Error("Invalid Boltz signature in checkpoint transaction");
3126
3103
  }
3127
3104
  const signedRefundTx = await identity.sign(unsignedRefundTx);
3128
3105
  const signedCheckpointTx = await identity.sign(unsignedCheckpointTx);
3129
- const combinedSignedRefundTx = (0, import_sdk7.combineTapscriptSigs)(
3130
- boltzSignedRefundTx,
3131
- signedRefundTx
3132
- );
3106
+ const combinedSignedRefundTx = (0, import_sdk7.combineTapscriptSigs)(boltzSignedRefundTx, signedRefundTx);
3133
3107
  const combinedSignedCheckpointTx = (0, import_sdk7.combineTapscriptSigs)(
3134
3108
  boltzSignedCheckpointTx,
3135
3109
  signedCheckpointTx
@@ -3153,25 +3127,16 @@ var refundVHTLCwithOffchainTx = async (swapId, identity, arkProvider, boltzXOnly
3153
3127
  `Expected one signed checkpoint transaction, got ${signedCheckpointTxs.length}`
3154
3128
  );
3155
3129
  }
3156
- const serverSignedCheckpointTx = import_sdk7.Transaction.fromPSBT(
3157
- import_base8.base64.decode(signedCheckpointTxs[0])
3158
- );
3130
+ const serverSignedCheckpointTx = import_sdk7.Transaction.fromPSBT(import_base8.base64.decode(signedCheckpointTxs[0]));
3159
3131
  const serverPubkeyHex = import_base8.hex.encode(serverXOnlyPublicKey);
3160
- if (!verifySignatures(
3161
- serverSignedCheckpointTx,
3162
- 0,
3163
- [serverPubkeyHex],
3164
- checkpointLeafHash
3165
- )) {
3132
+ if (!verifySignatures(serverSignedCheckpointTx, 0, [serverPubkeyHex], checkpointLeafHash)) {
3166
3133
  throw new Error("Invalid server signature in checkpoint transaction");
3167
3134
  }
3168
3135
  const finalCheckpointTx = (0, import_sdk7.combineTapscriptSigs)(
3169
3136
  combinedSignedCheckpointTx,
3170
3137
  serverSignedCheckpointTx
3171
3138
  );
3172
- await arkProvider.finalizeTx(arkTxid, [
3173
- import_base8.base64.encode(finalCheckpointTx.toPSBT())
3174
- ]);
3139
+ await arkProvider.finalizeTx(arkTxid, [import_base8.base64.encode(finalCheckpointTx.toPSBT())]);
3175
3140
  };
3176
3141
  function scriptFromTapLeafScript(leaf) {
3177
3142
  return leaf[1].subarray(0, leaf[1].length - 1);
@@ -3179,21 +3144,21 @@ function scriptFromTapLeafScript(leaf) {
3179
3144
 
3180
3145
  // src/arkade-swaps.ts
3181
3146
  var dedupeVtxos = (vtxos) => [
3182
- ...new Map(
3183
- vtxos.map((vtxo) => [`${vtxo.txid}:${vtxo.vout}`, vtxo])
3184
- ).values()
3147
+ ...new Map(vtxos.map((vtxo) => [`${vtxo.txid}:${vtxo.vout}`, vtxo])).values()
3185
3148
  ];
3186
3149
  var hasNonEmptyString = (value) => typeof value === "string" && value.length > 0;
3187
3150
  var canRecoverViaBoltz3of3 = (refundableVtxos, swap) => {
3188
3151
  const hasRequiredSwapMetadata = hasNonEmptyString(swap.id) && hasNonEmptyString(swap.request.refundPublicKey) && hasNonEmptyString(swap.response.address) && hasNonEmptyString(swap.response.claimPublicKey) && !!swap.response.timeoutBlockHeights;
3189
3152
  if (!hasRequiredSwapMetadata) return false;
3190
- return refundableVtxos.some(
3191
- (vtxo) => !vtxo.isSpent && !(0, import_sdk8.isRecoverable)(vtxo)
3192
- );
3153
+ return refundableVtxos.some((vtxo) => !vtxo.isSpent && !(0, import_sdk8.isRecoverable)(vtxo));
3193
3154
  };
3194
3155
  var isSubmarineRefundLocktimeReached = (refundTimestamp) => Math.floor(Date.now() / 1e3) >= refundTimestamp;
3195
3156
  var CLAIM_VTXO_RETRY_ATTEMPTS = 3;
3196
3157
  var CLAIM_VTXO_RETRY_DELAY_MS = 500;
3158
+ var quoteOptionsForSwap = (swap) => {
3159
+ const amount = swap.response?.claimDetails?.amount;
3160
+ return typeof amount === "number" ? { minAcceptableAmount: amount } : void 0;
3161
+ };
3197
3162
  var ArkadeSwaps = class _ArkadeSwaps {
3198
3163
  /** The Arkade wallet instance used for signing and address generation. */
3199
3164
  wallet;
@@ -3229,10 +3194,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
3229
3194
  return new _ArkadeSwaps(config);
3230
3195
  }
3231
3196
  const arkProvider = config.arkProvider ?? config.wallet.arkProvider;
3232
- if (!arkProvider)
3233
- throw new Error(
3234
- "Ark provider is required either in wallet or config."
3235
- );
3197
+ if (!arkProvider) throw new Error("Ark provider is required either in wallet or config.");
3236
3198
  const arkInfo = await arkProvider.getInfo();
3237
3199
  const network = arkInfo.network;
3238
3200
  const swapProvider = new BoltzSwapProvider({ network });
@@ -3243,16 +3205,11 @@ var ArkadeSwaps = class _ArkadeSwaps {
3243
3205
  if (!config.swapProvider) throw new Error("Swap provider is required.");
3244
3206
  this.wallet = config.wallet;
3245
3207
  const arkProvider = config.arkProvider ?? config.wallet.arkProvider;
3246
- if (!arkProvider)
3247
- throw new Error(
3248
- "Ark provider is required either in wallet or config."
3249
- );
3208
+ if (!arkProvider) throw new Error("Ark provider is required either in wallet or config.");
3250
3209
  this.arkProvider = arkProvider;
3251
3210
  const indexerProvider = config.indexerProvider ?? config.wallet.indexerProvider;
3252
3211
  if (!indexerProvider)
3253
- throw new Error(
3254
- "Indexer provider is required either in wallet or config."
3255
- );
3212
+ throw new Error("Indexer provider is required either in wallet or config.");
3256
3213
  this.indexerProvider = indexerProvider;
3257
3214
  this.swapProvider = config.swapProvider;
3258
3215
  if (config.swapRepository) {
@@ -3263,10 +3220,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
3263
3220
  if (config.swapManager !== false) {
3264
3221
  const swapManagerConfig = !config.swapManager || config.swapManager === true ? {} : config.swapManager;
3265
3222
  const shouldAutostart = swapManagerConfig.autoStart ?? true;
3266
- this.swapManager = new SwapManager(
3267
- this.swapProvider,
3268
- swapManagerConfig
3269
- );
3223
+ this.swapManager = new SwapManager(this.swapProvider, swapManagerConfig);
3270
3224
  this.swapManager.setCallbacks({
3271
3225
  claim: async (swap) => {
3272
3226
  await this.claimVHTLC(swap);
@@ -3281,7 +3235,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
3281
3235
  await this.claimBtc(swap);
3282
3236
  },
3283
3237
  refundArk: async (swap) => {
3284
- await this.refundArk(swap);
3238
+ return this.refundArk(swap);
3285
3239
  },
3286
3240
  signServerClaim: async (swap) => {
3287
3241
  await this.signCooperativeClaimForServer(swap);
@@ -3414,19 +3368,15 @@ var ArkadeSwaps = class _ArkadeSwaps {
3414
3368
  * @throws {SwapError} If amount is <= 0 or key retrieval fails.
3415
3369
  */
3416
3370
  async createReverseSwap(args) {
3417
- if (args.amount <= 0)
3418
- throw new SwapError({ message: "Amount must be greater than 0" });
3419
- const claimPublicKey = import_base9.hex.encode(
3420
- await this.wallet.identity.compressedPublicKey()
3421
- );
3371
+ if (args.amount <= 0) throw new SwapError({ message: "Amount must be greater than 0" });
3372
+ const claimPublicKey = import_base9.hex.encode(await this.wallet.identity.compressedPublicKey());
3422
3373
  if (!claimPublicKey)
3423
3374
  throw new SwapError({
3424
3375
  message: "Failed to get claim public key from wallet"
3425
3376
  });
3426
3377
  const preimage = (0, import_utils3.randomBytes)(32);
3427
3378
  const preimageHash = import_base9.hex.encode((0, import_sha23.sha256)(preimage));
3428
- if (!preimageHash)
3429
- throw new SwapError({ message: "Failed to get preimage hash" });
3379
+ if (!preimageHash) throw new SwapError({ message: "Failed to get preimage hash" });
3430
3380
  const swapRequest = {
3431
3381
  invoiceAmount: args.amount,
3432
3382
  claimPublicKey,
@@ -3456,18 +3406,14 @@ var ArkadeSwaps = class _ArkadeSwaps {
3456
3406
  */
3457
3407
  async claimVHTLC(pendingSwap) {
3458
3408
  if (!pendingSwap.preimage)
3459
- throw new Error(
3460
- `Swap ${pendingSwap.id}: preimage is required to claim VHTLC`
3461
- );
3409
+ throw new Error(`Swap ${pendingSwap.id}: preimage is required to claim VHTLC`);
3462
3410
  const {
3463
3411
  refundPublicKey,
3464
3412
  lockupAddress,
3465
3413
  timeoutBlockHeights: vhtlcTimeouts
3466
3414
  } = pendingSwap.response;
3467
3415
  if (!refundPublicKey || !lockupAddress || !vhtlcTimeouts)
3468
- throw new Error(
3469
- `Swap ${pendingSwap.id}: incomplete reverse swap response`
3470
- );
3416
+ throw new Error(`Swap ${pendingSwap.id}: incomplete reverse swap response`);
3471
3417
  const preimage = import_base9.hex.decode(pendingSwap.preimage);
3472
3418
  const arkInfo = await this.arkProvider.getInfo();
3473
3419
  const address = await this.wallet.getAddress();
@@ -3502,58 +3448,67 @@ var ArkadeSwaps = class _ArkadeSwaps {
3502
3448
  throw new Error(
3503
3449
  `Swap ${pendingSwap.id}: VHTLC address mismatch. Expected ${lockupAddress}, got ${vhtlcAddress}`
3504
3450
  );
3505
- let vtxo;
3451
+ let unspentVtxos = [];
3452
+ let rawVtxos = [];
3506
3453
  for (let attempt = 1; attempt <= CLAIM_VTXO_RETRY_ATTEMPTS; attempt++) {
3507
- const { vtxos } = await this.indexerProvider.getVtxos({
3454
+ const result = await this.indexerProvider.getVtxos({
3508
3455
  scripts: [import_base9.hex.encode(vhtlcScript.pkScript)]
3509
3456
  });
3510
- if (vtxos.length > 0) {
3511
- vtxo = vtxos[0];
3457
+ rawVtxos = result.vtxos;
3458
+ unspentVtxos = result.vtxos.filter((vtxo) => !vtxo.isSpent);
3459
+ if (unspentVtxos.length > 0) {
3512
3460
  break;
3513
3461
  }
3514
3462
  if (attempt < CLAIM_VTXO_RETRY_ATTEMPTS) {
3515
- await new Promise(
3516
- (resolve) => setTimeout(resolve, CLAIM_VTXO_RETRY_DELAY_MS)
3517
- );
3463
+ await new Promise((resolve) => setTimeout(resolve, CLAIM_VTXO_RETRY_DELAY_MS));
3518
3464
  }
3519
3465
  }
3520
- if (!vtxo) {
3521
- throw new Error(
3522
- `Swap ${pendingSwap.id}: no spendable virtual coins found`
3523
- );
3524
- }
3525
- if (vtxo.isSpent) {
3466
+ if (unspentVtxos.length === 0) {
3467
+ if (rawVtxos.length === 0) {
3468
+ throw new Error(`Swap ${pendingSwap.id}: no spendable virtual coins found`);
3469
+ }
3526
3470
  throw new Error(`Swap ${pendingSwap.id}: VHTLC is already spent`);
3527
3471
  }
3528
- const input = {
3529
- ...vtxo,
3530
- tapLeafScript: vhtlcScript.claim(),
3531
- tapTree: vhtlcScript.encode()
3532
- };
3533
- const output = {
3534
- amount: BigInt(vtxo.value),
3535
- script: import_sdk8.ArkAddress.decode(address).pkScript
3536
- };
3537
- const vhtlcIdentity = claimVHTLCIdentity(
3538
- this.wallet.identity,
3539
- preimage
3540
- );
3541
- let finalStatus;
3542
- if ((0, import_sdk8.isRecoverable)(vtxo)) {
3543
- await this.joinBatch(vhtlcIdentity, input, output, arkInfo);
3544
- finalStatus = "transaction.claimed";
3545
- } else {
3546
- await claimVHTLCwithOffchainTx(
3547
- vhtlcIdentity,
3548
- vhtlcScript,
3549
- serverXOnly,
3550
- input,
3551
- output,
3552
- arkInfo,
3553
- this.arkProvider
3472
+ const vhtlcIdentity = claimVHTLCIdentity(this.wallet.identity, preimage);
3473
+ const outputScript = import_sdk8.ArkAddress.decode(address).pkScript;
3474
+ const claimErrors = [];
3475
+ let usedOffchainClaim = false;
3476
+ for (const vtxo of unspentVtxos) {
3477
+ const input = {
3478
+ ...vtxo,
3479
+ tapLeafScript: vhtlcScript.claim(),
3480
+ tapTree: vhtlcScript.encode()
3481
+ };
3482
+ const output = {
3483
+ amount: BigInt(vtxo.value),
3484
+ script: outputScript
3485
+ };
3486
+ try {
3487
+ if ((0, import_sdk8.isRecoverable)(vtxo)) {
3488
+ await this.joinBatch(vhtlcIdentity, input, output, arkInfo);
3489
+ } else {
3490
+ await claimVHTLCwithOffchainTx(
3491
+ vhtlcIdentity,
3492
+ vhtlcScript,
3493
+ serverXOnly,
3494
+ input,
3495
+ output,
3496
+ arkInfo,
3497
+ this.arkProvider
3498
+ );
3499
+ usedOffchainClaim = true;
3500
+ }
3501
+ } catch (error) {
3502
+ claimErrors.push({ vtxo, error });
3503
+ }
3504
+ }
3505
+ if (claimErrors.length > 0) {
3506
+ const details = claimErrors.map(({ vtxo, error }) => `${vtxo.txid}:${vtxo.vout} (${error.message})`).join("; ");
3507
+ throw new Error(
3508
+ `Swap ${pendingSwap.id}: failed to claim ${claimErrors.length}/${unspentVtxos.length} VTXOs: ${details}`
3554
3509
  );
3555
- finalStatus = (await this.getSwapStatus(pendingSwap.id)).status;
3556
3510
  }
3511
+ const finalStatus = usedOffchainClaim ? (await this.getSwapStatus(pendingSwap.id)).status : "transaction.claimed";
3557
3512
  await updateReverseSwapStatus(
3558
3513
  pendingSwap,
3559
3514
  finalStatus,
@@ -3656,9 +3611,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
3656
3611
  async sendLightningPayment(args) {
3657
3612
  const pendingSwap = await this.createSubmarineSwap(args);
3658
3613
  if (!pendingSwap.response.address)
3659
- throw new Error(
3660
- `Swap ${pendingSwap.id}: missing address in submarine swap response`
3661
- );
3614
+ throw new Error(`Swap ${pendingSwap.id}: missing address in submarine swap response`);
3662
3615
  await this.savePendingSubmarineSwap(pendingSwap);
3663
3616
  const txid = await this.wallet.send({
3664
3617
  address: pendingSwap.response.address,
@@ -3691,9 +3644,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
3691
3644
  * @throws {SwapError} If invoice is missing or key retrieval fails.
3692
3645
  */
3693
3646
  async createSubmarineSwap(args) {
3694
- const refundPublicKey = import_base9.hex.encode(
3695
- await this.wallet.identity.compressedPublicKey()
3696
- );
3647
+ const refundPublicKey = import_base9.hex.encode(await this.wallet.identity.compressedPublicKey());
3697
3648
  if (!refundPublicKey)
3698
3649
  throw new SwapError({
3699
3650
  message: "Failed to get refund public key from wallet"
@@ -3731,9 +3682,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
3731
3682
  async buildSubmarineVHTLCContext(swap, arkInfo) {
3732
3683
  const preimageHash = swap.request.invoice ? getInvoicePaymentHash(swap.request.invoice) : swap.preimageHash;
3733
3684
  if (!preimageHash)
3734
- throw new Error(
3735
- `Swap ${swap.id}: preimage hash is required to refund VHTLC`
3736
- );
3685
+ throw new Error(`Swap ${swap.id}: preimage hash is required to refund VHTLC`);
3737
3686
  const resolvedArkInfo = arkInfo ?? await this.arkProvider.getInfo();
3738
3687
  const ourXOnlyPublicKey = normalizeToXOnlyKey(
3739
3688
  await this.wallet.identity.xOnlyPublicKey(),
@@ -3747,9 +3696,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
3747
3696
  );
3748
3697
  const { claimPublicKey, timeoutBlockHeights: vhtlcTimeouts } = swap.response;
3749
3698
  if (!claimPublicKey || !vhtlcTimeouts)
3750
- throw new Error(
3751
- `Swap ${swap.id}: incomplete submarine swap response`
3752
- );
3699
+ throw new Error(`Swap ${swap.id}: incomplete submarine swap response`);
3753
3700
  const boltzXOnlyPublicKey = normalizeToXOnlyKey(
3754
3701
  import_base9.hex.decode(claimPublicKey),
3755
3702
  "boltz",
@@ -3764,9 +3711,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
3764
3711
  timeoutBlockHeights: vhtlcTimeouts
3765
3712
  });
3766
3713
  if (!vhtlcScript.claimScript)
3767
- throw new Error(
3768
- `Swap ${swap.id}: failed to create VHTLC script for submarine swap`
3769
- );
3714
+ throw new Error(`Swap ${swap.id}: failed to create VHTLC script for submarine swap`);
3770
3715
  if (vhtlcAddress !== swap.response.address)
3771
3716
  throw new Error(
3772
3717
  `VHTLC address mismatch for swap ${swap.id}: expected ${swap.response.address}, got ${vhtlcAddress}`
@@ -3805,10 +3750,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
3805
3750
  recoverableOnly: true
3806
3751
  })
3807
3752
  ]);
3808
- const refundableVtxos = dedupeVtxos([
3809
- ...spendableResult.vtxos,
3810
- ...recoverableResult.vtxos
3811
- ]);
3753
+ const refundableVtxos = dedupeVtxos([...spendableResult.vtxos, ...recoverableResult.vtxos]);
3812
3754
  let diagnostic;
3813
3755
  if (refundableVtxos.length === 0) {
3814
3756
  const { vtxos: allVtxos } = await this.indexerProvider.getVtxos({
@@ -3828,13 +3770,8 @@ var ArkadeSwaps = class _ArkadeSwaps {
3828
3770
  submarineRecoveryInfoFromLookup(swap, lookup) {
3829
3771
  const { refundableVtxos, diagnostic, vhtlcTimeouts } = lookup;
3830
3772
  if (refundableVtxos.length > 0) {
3831
- const cltvSatisfied = isSubmarineRefundLocktimeReached(
3832
- vhtlcTimeouts.refund
3833
- );
3834
- const amountSats = refundableVtxos.reduce(
3835
- (sum, vtxo) => sum + Number(vtxo.value),
3836
- 0
3837
- );
3773
+ const cltvSatisfied = isSubmarineRefundLocktimeReached(vhtlcTimeouts.refund);
3774
+ const amountSats = refundableVtxos.reduce((sum, vtxo) => sum + Number(vtxo.value), 0);
3838
3775
  const isRecoverable2 = cltvSatisfied || canRecoverViaBoltz3of3(refundableVtxos, swap);
3839
3776
  return {
3840
3777
  swap,
@@ -3898,19 +3835,13 @@ var ArkadeSwaps = class _ArkadeSwaps {
3898
3835
  );
3899
3836
  }
3900
3837
  if (diagnostic.allSpent) {
3901
- throw new Error(
3902
- `Swap ${pendingSwap.id}: VHTLC is already spent`
3903
- );
3838
+ throw new Error(`Swap ${pendingSwap.id}: VHTLC is already spent`);
3904
3839
  }
3905
- throw new Error(
3906
- `Swap ${pendingSwap.id}: VHTLC has no refundable VTXOs yet`
3907
- );
3840
+ throw new Error(`Swap ${pendingSwap.id}: VHTLC has no refundable VTXOs yet`);
3908
3841
  }
3909
3842
  const outputScript = import_sdk8.ArkAddress.decode(address).pkScript;
3910
3843
  const refundWithoutReceiverLeaf = vhtlcScript.refundWithoutReceiver();
3911
- const cltvSatisfied = isSubmarineRefundLocktimeReached(
3912
- vhtlcTimeouts.refund
3913
- );
3844
+ const cltvSatisfied = isSubmarineRefundLocktimeReached(vhtlcTimeouts.refund);
3914
3845
  let boltzCallCount = 0;
3915
3846
  let sweptCount = 0;
3916
3847
  let skippedCount = 0;
@@ -3952,6 +3883,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
3952
3883
  if (boltzCallCount > 0) {
3953
3884
  await new Promise((r) => setTimeout(r, 2e3));
3954
3885
  }
3886
+ boltzCallCount++;
3955
3887
  await refundVHTLCwithOffchainTx(
3956
3888
  pendingSwap.id,
3957
3889
  this.wallet.identity,
@@ -3962,11 +3894,8 @@ var ArkadeSwaps = class _ArkadeSwaps {
3962
3894
  input,
3963
3895
  output,
3964
3896
  arkInfo,
3965
- this.swapProvider.refundSubmarineSwap.bind(
3966
- this.swapProvider
3967
- )
3897
+ this.swapProvider.refundSubmarineSwap.bind(this.swapProvider)
3968
3898
  );
3969
- boltzCallCount++;
3970
3899
  sweptCount++;
3971
3900
  } catch (error) {
3972
3901
  if (!(error instanceof BoltzRefundError)) {
@@ -3987,13 +3916,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
3987
3916
  tapLeafScript: refundWithoutReceiverLeaf,
3988
3917
  tapTree: vhtlcScript.encode()
3989
3918
  };
3990
- await this.joinBatch(
3991
- this.wallet.identity,
3992
- fallbackInput,
3993
- output,
3994
- arkInfo,
3995
- false
3996
- );
3919
+ await this.joinBatch(this.wallet.identity, fallbackInput, output, arkInfo, false);
3997
3920
  sweptCount++;
3998
3921
  }
3999
3922
  }
@@ -4089,10 +4012,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
4089
4012
  try {
4090
4013
  return {
4091
4014
  swap,
4092
- context: await this.buildSubmarineVHTLCContext(
4093
- swap,
4094
- arkInfo
4095
- )
4015
+ context: await this.buildSubmarineVHTLCContext(swap, arkInfo)
4096
4016
  };
4097
4017
  } catch (err) {
4098
4018
  return {
@@ -4105,9 +4025,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
4105
4025
  const valid = prepared.filter(
4106
4026
  (item) => "context" in item
4107
4027
  );
4108
- const scripts = [
4109
- ...new Set(valid.map(({ context }) => context.vhtlcPkScriptHex))
4110
- ];
4028
+ const scripts = [...new Set(valid.map(({ context }) => context.vhtlcPkScriptHex))];
4111
4029
  const refundableByScript = /* @__PURE__ */ new Map();
4112
4030
  if (scripts.length > 0) {
4113
4031
  const [spendableResult, recoverableResult] = await Promise.all([
@@ -4142,9 +4060,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
4142
4060
  error: item.error
4143
4061
  };
4144
4062
  }
4145
- const refundableVtxos = refundableByScript.get(
4146
- item.context.vhtlcPkScriptHex.toLowerCase()
4147
- ) ?? [];
4063
+ const refundableVtxos = refundableByScript.get(item.context.vhtlcPkScriptHex.toLowerCase()) ?? [];
4148
4064
  return this.submarineRecoveryInfoFromLookup(item.swap, {
4149
4065
  ...item.context,
4150
4066
  refundableVtxos
@@ -4323,9 +4239,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
4323
4239
  */
4324
4240
  async waitAndClaimBtc(pendingSwap) {
4325
4241
  if (this.swapManager && await this.swapManager.hasSwap(pendingSwap.id)) {
4326
- const { txid } = await this.swapManager.waitForSwapCompletion(
4327
- pendingSwap.id
4328
- );
4242
+ const { txid } = await this.swapManager.waitForSwapCompletion(pendingSwap.id);
4329
4243
  return { txid };
4330
4244
  }
4331
4245
  return new Promise((resolve, reject) => {
@@ -4352,24 +4266,25 @@ var ArkadeSwaps = class _ArkadeSwaps {
4352
4266
  }
4353
4267
  case "transaction.claimed":
4354
4268
  await updateSwapStatus();
4355
- const claimedStatus = await this.getSwapStatus(
4356
- pendingSwap.id
4357
- );
4269
+ const claimedStatus = await this.getSwapStatus(pendingSwap.id);
4358
4270
  resolve({
4359
4271
  txid: claimedStatus.transaction?.id ?? ""
4360
4272
  });
4361
4273
  break;
4362
4274
  case "transaction.lockupFailed":
4363
4275
  await updateSwapStatus();
4364
- await this.quoteSwap(swap.response.id).catch((err) => {
4365
- reject(
4366
- new SwapError({
4367
- message: `Failed to renegotiate quote: ${err.message}`,
4368
- isRefundable: true,
4369
- pendingSwap: swap
4370
- })
4371
- );
4372
- });
4276
+ await this.quoteSwap(swap.response.id, quoteOptionsForSwap(swap)).catch(
4277
+ (err) => {
4278
+ reject(
4279
+ new SwapError({
4280
+ message: `Failed to renegotiate quote: ${err.message}`,
4281
+ isRefundable: true,
4282
+ pendingSwap: swap,
4283
+ cause: err
4284
+ })
4285
+ );
4286
+ }
4287
+ );
4373
4288
  break;
4374
4289
  case "swap.expired":
4375
4290
  await updateSwapStatus();
@@ -4407,30 +4322,18 @@ var ArkadeSwaps = class _ArkadeSwaps {
4407
4322
  */
4408
4323
  async claimBtc(pendingSwap) {
4409
4324
  if (!pendingSwap.toAddress)
4410
- throw new Error(
4411
- `Swap ${pendingSwap.id}: destination address is required`
4412
- );
4325
+ throw new Error(`Swap ${pendingSwap.id}: destination address is required`);
4413
4326
  if (!pendingSwap.response.claimDetails.swapTree)
4414
- throw new Error(
4415
- `Swap ${pendingSwap.id}: missing swap tree in claim details`
4416
- );
4327
+ throw new Error(`Swap ${pendingSwap.id}: missing swap tree in claim details`);
4417
4328
  if (!pendingSwap.response.claimDetails.serverPublicKey)
4418
- throw new Error(
4419
- `Swap ${pendingSwap.id}: missing server public key in claim details`
4420
- );
4329
+ throw new Error(`Swap ${pendingSwap.id}: missing server public key in claim details`);
4421
4330
  const swapStatus = await this.getSwapStatus(pendingSwap.id);
4422
4331
  if (!swapStatus.transaction?.hex)
4423
- throw new Error(
4424
- `Swap ${pendingSwap.id}: BTC transaction hex is required`
4425
- );
4426
- const lockupTx = import_btc_signer5.Transaction.fromRaw(
4427
- import_base9.hex.decode(swapStatus.transaction.hex)
4428
- );
4332
+ throw new Error(`Swap ${pendingSwap.id}: BTC transaction hex is required`);
4333
+ const lockupTx = import_btc_signer5.Transaction.fromRaw(import_base9.hex.decode(swapStatus.transaction.hex));
4429
4334
  const arkInfo = await this.arkProvider.getInfo();
4430
4335
  const network = arkInfo.network === "bitcoin" ? import_utils4.NETWORK : arkInfo.network === "mutinynet" ? MUTINYNET_NETWORK : REGTEST_NETWORK;
4431
- const swapTree = deserializeSwapTree(
4432
- pendingSwap.response.claimDetails.swapTree
4433
- );
4336
+ const swapTree = deserializeSwapTree(pendingSwap.response.claimDetails.swapTree);
4434
4337
  const musig = tweakMusig(
4435
4338
  create(import_base9.hex.decode(pendingSwap.ephemeralKey), [
4436
4339
  import_base9.hex.decode(pendingSwap.response.claimDetails.serverPublicKey),
@@ -4451,19 +4354,14 @@ var ArkadeSwaps = class _ArkadeSwaps {
4451
4354
  vout: swapOutput.vout,
4452
4355
  transactionId: lockupTx.id
4453
4356
  },
4454
- import_btc_signer5.OutScript.encode(
4455
- (0, import_btc_signer5.Address)(network).decode(pendingSwap.toAddress)
4456
- ),
4357
+ import_btc_signer5.OutScript.encode((0, import_btc_signer5.Address)(network).decode(pendingSwap.toAddress)),
4457
4358
  feeToDeliverExactAmount > fee ? feeToDeliverExactAmount : fee
4458
4359
  )
4459
4360
  );
4460
4361
  const musigMessage = musig.message(
4461
- claimTx.preimageWitnessV1(
4462
- 0,
4463
- [swapOutput.script],
4464
- import_btc_signer5.SigHash.DEFAULT,
4465
- [swapOutput.amount]
4466
- )
4362
+ claimTx.preimageWitnessV1(0, [swapOutput.script], import_btc_signer5.SigHash.DEFAULT, [
4363
+ swapOutput.amount
4364
+ ])
4467
4365
  ).generateNonce();
4468
4366
  const signedTxData = await this.swapProvider.postChainClaimDetails(
4469
4367
  pendingSwap.response.id,
@@ -4477,14 +4375,10 @@ var ArkadeSwaps = class _ArkadeSwaps {
4477
4375
  }
4478
4376
  );
4479
4377
  if (!signedTxData.pubNonce || !signedTxData.partialSignature)
4480
- throw new Error(
4481
- `Swap ${pendingSwap.id}: invalid signature data from server`
4482
- );
4378
+ throw new Error(`Swap ${pendingSwap.id}: invalid signature data from server`);
4483
4379
  const musigSession = musigMessage.aggregateNonces([
4484
4380
  [
4485
- import_base9.hex.decode(
4486
- pendingSwap.response.claimDetails.serverPublicKey
4487
- ),
4381
+ import_base9.hex.decode(pendingSwap.response.claimDetails.serverPublicKey),
4488
4382
  import_base9.hex.decode(signedTxData.pubNonce)
4489
4383
  ]
4490
4384
  ]).initializeSession();
@@ -4499,18 +4393,23 @@ var ArkadeSwaps = class _ArkadeSwaps {
4499
4393
  await this.swapProvider.postBtcTransaction(claimTx.hex);
4500
4394
  }
4501
4395
  /**
4502
- * When an ARK to BTC swap fails, refund sats on ARK chain by claiming the VHTLC.
4396
+ * When an ARK to BTC swap fails, refund every unspent VTXO at the chain
4397
+ * swap's ARK lockup address.
4398
+ *
4399
+ * Path selection per VTXO:
4400
+ * - CLTV has elapsed → `refundWithoutReceiver` via `joinBatch` (no Boltz).
4401
+ * - Pre-CLTV recoverable → skipped (Boltz can't co-sign swept-batch refund).
4402
+ * - Pre-CLTV non-recoverable → cooperative 3-of-3 refund via Boltz.
4403
+ *
4503
4404
  * @param pendingSwap - The pending chain swap to refund.
4405
+ * @returns Counts of VTXOs swept vs. deferred. A `swept: 0` outcome means
4406
+ * the call was a no-op — callers should retry after CLTV.
4504
4407
  */
4505
4408
  async refundArk(pendingSwap) {
4506
4409
  if (!pendingSwap.response.lockupDetails.serverPublicKey)
4507
- throw new Error(
4508
- `Swap ${pendingSwap.id}: missing server public key in lockup details`
4509
- );
4410
+ throw new Error(`Swap ${pendingSwap.id}: missing server public key in lockup details`);
4510
4411
  if (!pendingSwap.response.lockupDetails.timeouts)
4511
- throw new Error(
4512
- `Swap ${pendingSwap.id}: missing timeouts in lockup details`
4513
- );
4412
+ throw new Error(`Swap ${pendingSwap.id}: missing timeouts in lockup details`);
4514
4413
  const arkInfo = await this.arkProvider.getInfo();
4515
4414
  const address = await this.wallet.getAddress();
4516
4415
  const ourXOnlyPublicKey = normalizeToXOnlyKey(
@@ -4528,21 +4427,6 @@ var ArkadeSwaps = class _ArkadeSwaps {
4528
4427
  "boltz",
4529
4428
  pendingSwap.id
4530
4429
  );
4531
- const vhtlcPkScript = import_sdk8.ArkAddress.decode(
4532
- pendingSwap.response.lockupDetails.lockupAddress
4533
- ).pkScript;
4534
- const { vtxos } = await this.indexerProvider.getVtxos({
4535
- scripts: [import_base9.hex.encode(vhtlcPkScript)]
4536
- });
4537
- if (vtxos.length === 0) {
4538
- throw new Error(
4539
- `Swap ${pendingSwap.id}: VHTLC not found for address ${pendingSwap.response.lockupDetails.lockupAddress}`
4540
- );
4541
- }
4542
- const vtxo = vtxos[0];
4543
- if (vtxo.isSpent) {
4544
- throw new Error(`Swap ${pendingSwap.id}: VHTLC is already spent`);
4545
- }
4546
4430
  const { vhtlcAddress, vhtlcScript } = this.createVHTLCScript({
4547
4431
  network: arkInfo.network,
4548
4432
  preimageHash: import_base9.hex.decode(pendingSwap.request.preimageHash),
@@ -4552,45 +4436,111 @@ var ArkadeSwaps = class _ArkadeSwaps {
4552
4436
  timeoutBlockHeights: pendingSwap.response.lockupDetails.timeouts
4553
4437
  });
4554
4438
  if (!vhtlcScript.refundScript)
4555
- throw new Error(
4556
- `Swap ${pendingSwap.id}: failed to create VHTLC script for chain swap`
4557
- );
4439
+ throw new Error(`Swap ${pendingSwap.id}: failed to create VHTLC script for chain swap`);
4558
4440
  if (pendingSwap.response.lockupDetails.lockupAddress !== vhtlcAddress) {
4559
4441
  throw new SwapError({
4560
4442
  message: "Unable to claim: invalid VHTLC address"
4561
4443
  });
4562
4444
  }
4563
- const isRecoverableVtxo = (0, import_sdk8.isRecoverable)(vtxo);
4564
- const input = {
4565
- ...vtxo,
4566
- tapLeafScript: isRecoverableVtxo ? vhtlcScript.refundWithoutReceiver() : vhtlcScript.refund(),
4567
- tapTree: vhtlcScript.encode()
4568
- };
4569
- const output = {
4570
- amount: BigInt(vtxo.value),
4571
- script: import_sdk8.ArkAddress.decode(address).pkScript
4572
- };
4573
- if (isRecoverableVtxo) {
4574
- await this.joinBatch(this.wallet.identity, input, output, arkInfo);
4575
- } else {
4576
- await refundVHTLCwithOffchainTx(
4577
- pendingSwap.id,
4578
- this.wallet.identity,
4579
- this.arkProvider,
4580
- boltzXOnlyPublicKey,
4581
- ourXOnlyPublicKey,
4582
- serverXOnlyPublicKey,
4583
- input,
4584
- output,
4585
- arkInfo,
4586
- this.swapProvider.refundChainSwap.bind(this.swapProvider)
4445
+ const { vtxos } = await this.indexerProvider.getVtxos({
4446
+ scripts: [import_base9.hex.encode(vhtlcScript.pkScript)]
4447
+ });
4448
+ if (vtxos.length === 0) {
4449
+ throw new Error(
4450
+ `Swap ${pendingSwap.id}: VHTLC not found for address ${pendingSwap.response.lockupDetails.lockupAddress}`
4587
4451
  );
4588
4452
  }
4453
+ const unspentVtxos = vtxos.filter((vtxo) => !vtxo.isSpent);
4454
+ if (unspentVtxos.length === 0) {
4455
+ throw new Error(`Swap ${pendingSwap.id}: VHTLC is already spent`);
4456
+ }
4457
+ const outputScript = import_sdk8.ArkAddress.decode(address).pkScript;
4458
+ const refundWithoutReceiverLeaf = vhtlcScript.refundWithoutReceiver();
4459
+ const refundLocktime = pendingSwap.response.lockupDetails.timeouts.refund;
4460
+ let boltzCallCount = 0;
4461
+ let sweptCount = 0;
4462
+ let skippedCount = 0;
4463
+ for (const vtxo of unspentVtxos) {
4464
+ const isRecoverableVtxo = (0, import_sdk8.isRecoverable)(vtxo);
4465
+ const output = {
4466
+ amount: BigInt(vtxo.value),
4467
+ script: outputScript
4468
+ };
4469
+ if (isSubmarineRefundLocktimeReached(refundLocktime)) {
4470
+ const input2 = {
4471
+ ...vtxo,
4472
+ tapLeafScript: refundWithoutReceiverLeaf,
4473
+ tapTree: vhtlcScript.encode()
4474
+ };
4475
+ await this.joinBatch(
4476
+ this.wallet.identity,
4477
+ input2,
4478
+ output,
4479
+ arkInfo,
4480
+ isRecoverableVtxo
4481
+ );
4482
+ sweptCount++;
4483
+ continue;
4484
+ }
4485
+ if (isRecoverableVtxo) {
4486
+ logger.error(
4487
+ `Swap ${pendingSwap.id}: recoverable VTXO ${vtxo.txid}:${vtxo.vout} cannot be refunded yet \u2014 refundWithoutReceiver locktime has not passed (refundLocktime=${refundLocktime}, currentTimestamp=${Math.floor(Date.now() / 1e3)}). Refund will be retried after locktime.`
4488
+ );
4489
+ skippedCount++;
4490
+ continue;
4491
+ }
4492
+ const input = {
4493
+ ...vtxo,
4494
+ tapLeafScript: vhtlcScript.refund(),
4495
+ tapTree: vhtlcScript.encode()
4496
+ };
4497
+ try {
4498
+ if (boltzCallCount > 0) {
4499
+ await new Promise((r) => setTimeout(r, 2e3));
4500
+ }
4501
+ boltzCallCount++;
4502
+ await refundVHTLCwithOffchainTx(
4503
+ pendingSwap.id,
4504
+ this.wallet.identity,
4505
+ this.arkProvider,
4506
+ boltzXOnlyPublicKey,
4507
+ ourXOnlyPublicKey,
4508
+ serverXOnlyPublicKey,
4509
+ input,
4510
+ output,
4511
+ arkInfo,
4512
+ this.swapProvider.refundChainSwap.bind(this.swapProvider)
4513
+ );
4514
+ sweptCount++;
4515
+ } catch (error) {
4516
+ if (!(error instanceof BoltzRefundError)) {
4517
+ throw error;
4518
+ }
4519
+ if (!isSubmarineRefundLocktimeReached(refundLocktime)) {
4520
+ logger.error(
4521
+ `Swap ${pendingSwap.id}: Boltz rejected VTXO outpoint and refundWithoutReceiver locktime has not passed yet (currentTimestamp=${Math.floor(Date.now() / 1e3)}, locktime=${refundLocktime}). Refund will be retried after locktime.`
4522
+ );
4523
+ skippedCount++;
4524
+ continue;
4525
+ }
4526
+ logger.warn(
4527
+ `Swap ${pendingSwap.id}: Boltz rejected VTXO outpoint, falling back to refundWithoutReceiver via joinBatch`
4528
+ );
4529
+ const fallbackInput = {
4530
+ ...vtxo,
4531
+ tapLeafScript: refundWithoutReceiverLeaf,
4532
+ tapTree: vhtlcScript.encode()
4533
+ };
4534
+ await this.joinBatch(this.wallet.identity, fallbackInput, output, arkInfo, false);
4535
+ sweptCount++;
4536
+ }
4537
+ }
4589
4538
  const finalStatus = await this.getSwapStatus(pendingSwap.id);
4590
4539
  await this.savePendingChainSwap({
4591
4540
  ...pendingSwap,
4592
4541
  status: finalStatus.status
4593
4542
  });
4543
+ return { swept: sweptCount, skipped: skippedCount };
4594
4544
  }
4595
4545
  // =========================================================================
4596
4546
  // Chain swaps: BTC -> ARK
@@ -4635,9 +4585,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
4635
4585
  */
4636
4586
  async waitAndClaimArk(pendingSwap) {
4637
4587
  if (this.swapManager && await this.swapManager.hasSwap(pendingSwap.id)) {
4638
- const { txid } = await this.swapManager.waitForSwapCompletion(
4639
- pendingSwap.id
4640
- );
4588
+ const { txid } = await this.swapManager.waitForSwapCompletion(pendingSwap.id);
4641
4589
  return { txid };
4642
4590
  }
4643
4591
  return new Promise((resolve, reject) => {
@@ -4658,37 +4606,33 @@ var ArkadeSwaps = class _ArkadeSwaps {
4658
4606
  break;
4659
4607
  case "transaction.claimed":
4660
4608
  await updateSwapStatus();
4661
- const claimedStatus = await this.getSwapStatus(
4662
- pendingSwap.id
4663
- );
4609
+ const claimedStatus = await this.getSwapStatus(pendingSwap.id);
4664
4610
  resolve({
4665
4611
  txid: claimedStatus.transaction?.id ?? ""
4666
4612
  });
4667
4613
  break;
4668
4614
  case "transaction.claim.pending":
4669
4615
  await updateSwapStatus();
4670
- await this.signCooperativeClaimForServer(swap).catch(
4616
+ await this.signCooperativeClaimForServer(swap).catch((err) => {
4617
+ logger.error(`Failed to sign cooperative claim for ${swap.id}:`, err);
4618
+ });
4619
+ break;
4620
+ case "transaction.lockupFailed":
4621
+ await updateSwapStatus();
4622
+ await this.quoteSwap(swap.response.id, quoteOptionsForSwap(swap)).catch(
4671
4623
  (err) => {
4672
- logger.error(
4673
- `Failed to sign cooperative claim for ${swap.id}:`,
4674
- err
4624
+ reject(
4625
+ new SwapError({
4626
+ message: `Failed to renegotiate quote: ${err.message}`,
4627
+ isRefundable: false,
4628
+ // TODO btc refund not implemented yet
4629
+ pendingSwap: swap,
4630
+ cause: err
4631
+ })
4675
4632
  );
4676
4633
  }
4677
4634
  );
4678
4635
  break;
4679
- case "transaction.lockupFailed":
4680
- await updateSwapStatus();
4681
- await this.quoteSwap(swap.response.id).catch((err) => {
4682
- reject(
4683
- new SwapError({
4684
- message: `Failed to renegotiate quote: ${err.message}`,
4685
- isRefundable: false,
4686
- // TODO btc refund not implemented yet
4687
- pendingSwap: swap
4688
- })
4689
- );
4690
- });
4691
- break;
4692
4636
  case "swap.expired":
4693
4637
  await updateSwapStatus();
4694
4638
  reject(
@@ -4728,17 +4672,11 @@ var ArkadeSwaps = class _ArkadeSwaps {
4728
4672
  */
4729
4673
  async claimArk(pendingSwap) {
4730
4674
  if (!pendingSwap.toAddress)
4731
- throw new Error(
4732
- `Swap ${pendingSwap.id}: destination address is required`
4733
- );
4675
+ throw new Error(`Swap ${pendingSwap.id}: destination address is required`);
4734
4676
  if (!pendingSwap.response.claimDetails.serverPublicKey)
4735
- throw new Error(
4736
- `Swap ${pendingSwap.id}: missing server public key in claim details`
4737
- );
4677
+ throw new Error(`Swap ${pendingSwap.id}: missing server public key in claim details`);
4738
4678
  if (!pendingSwap.response.claimDetails.timeouts)
4739
- throw new Error(
4740
- `Swap ${pendingSwap.id}: missing timeouts in claim details`
4741
- );
4679
+ throw new Error(`Swap ${pendingSwap.id}: missing timeouts in claim details`);
4742
4680
  const arkInfo = await this.arkProvider.getInfo();
4743
4681
  const preimage = import_base9.hex.decode(pendingSwap.preimage);
4744
4682
  const address = await this.wallet.getAddress();
@@ -4750,10 +4688,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
4750
4688
  pendingSwap.response.claimDetails.serverPublicKey,
4751
4689
  "sender"
4752
4690
  );
4753
- const serverXOnlyPublicKey = normalizeToXOnlyKey(
4754
- arkInfo.signerPubkey,
4755
- "server"
4756
- );
4691
+ const serverXOnlyPublicKey = normalizeToXOnlyKey(arkInfo.signerPubkey, "server");
4757
4692
  const { vhtlcAddress, vhtlcScript } = this.createVHTLCScript({
4758
4693
  network: arkInfo.network,
4759
4694
  preimageHash: import_base9.hex.decode(pendingSwap.request.preimageHash),
@@ -4763,9 +4698,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
4763
4698
  timeoutBlockHeights: pendingSwap.response.claimDetails.timeouts
4764
4699
  });
4765
4700
  if (!vhtlcScript.claimScript)
4766
- throw new Error(
4767
- `Swap ${pendingSwap.id}: failed to create VHTLC script for chain swap`
4768
- );
4701
+ throw new Error(`Swap ${pendingSwap.id}: failed to create VHTLC script for chain swap`);
4769
4702
  if (pendingSwap.response.claimDetails.lockupAddress !== vhtlcAddress) {
4770
4703
  throw new SwapError({
4771
4704
  message: "Unable to claim: invalid VHTLC address"
@@ -4782,15 +4715,11 @@ var ArkadeSwaps = class _ArkadeSwaps {
4782
4715
  break;
4783
4716
  }
4784
4717
  if (attempt < CLAIM_VTXO_RETRY_ATTEMPTS) {
4785
- await new Promise(
4786
- (resolve) => setTimeout(resolve, CLAIM_VTXO_RETRY_DELAY_MS)
4787
- );
4718
+ await new Promise((resolve) => setTimeout(resolve, CLAIM_VTXO_RETRY_DELAY_MS));
4788
4719
  }
4789
4720
  }
4790
4721
  if (!vtxo) {
4791
- throw new Error(
4792
- `Swap ${pendingSwap.id}: no spendable virtual coins found`
4793
- );
4722
+ throw new Error(`Swap ${pendingSwap.id}: no spendable virtual coins found`);
4794
4723
  }
4795
4724
  const input = {
4796
4725
  ...vtxo,
@@ -4801,10 +4730,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
4801
4730
  amount: BigInt(vtxo.value),
4802
4731
  script: import_sdk8.ArkAddress.decode(address).pkScript
4803
4732
  };
4804
- const vhtlcIdentity = claimVHTLCIdentity(
4805
- this.wallet.identity,
4806
- preimage
4807
- );
4733
+ const vhtlcIdentity = claimVHTLCIdentity(this.wallet.identity, preimage);
4808
4734
  if ((0, import_sdk8.isRecoverable)(vtxo)) {
4809
4735
  await this.joinBatch(vhtlcIdentity, input, output, arkInfo);
4810
4736
  } else {
@@ -4830,16 +4756,10 @@ var ArkadeSwaps = class _ArkadeSwaps {
4830
4756
  */
4831
4757
  async signCooperativeClaimForServer(pendingSwap) {
4832
4758
  if (!pendingSwap.response.lockupDetails.swapTree)
4833
- throw new Error(
4834
- `Swap ${pendingSwap.id}: missing swap tree in lockup details`
4835
- );
4759
+ throw new Error(`Swap ${pendingSwap.id}: missing swap tree in lockup details`);
4836
4760
  if (!pendingSwap.response.lockupDetails.serverPublicKey)
4837
- throw new Error(
4838
- `Swap ${pendingSwap.id}: missing server public key in lockup details`
4839
- );
4840
- const claimDetails = await this.swapProvider.getChainClaimDetails(
4841
- pendingSwap.id
4842
- );
4761
+ throw new Error(`Swap ${pendingSwap.id}: missing server public key in lockup details`);
4762
+ const claimDetails = await this.swapProvider.getChainClaimDetails(pendingSwap.id);
4843
4763
  const serverPubKey = pendingSwap.response.lockupDetails.serverPublicKey;
4844
4764
  if (claimDetails.publicKey !== serverPubKey) {
4845
4765
  throw new Error(
@@ -4855,9 +4775,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
4855
4775
  );
4856
4776
  const musigNonces = musig.message(import_base9.hex.decode(claimDetails.transactionHash)).generateNonce().aggregateNonces([
4857
4777
  [
4858
- import_base9.hex.decode(
4859
- pendingSwap.response.lockupDetails.serverPublicKey
4860
- ),
4778
+ import_base9.hex.decode(pendingSwap.response.lockupDetails.serverPublicKey),
4861
4779
  import_base9.hex.decode(claimDetails.pubNonce)
4862
4780
  ]
4863
4781
  ]).initializeSession();
@@ -4876,10 +4794,8 @@ var ArkadeSwaps = class _ArkadeSwaps {
4876
4794
  * @returns The transaction ID of the claim.
4877
4795
  */
4878
4796
  async waitAndClaimChain(pendingSwap) {
4879
- if (pendingSwap.request.to === "ARK")
4880
- return this.waitAndClaimArk(pendingSwap);
4881
- if (pendingSwap.request.to === "BTC")
4882
- return this.waitAndClaimBtc(pendingSwap);
4797
+ if (pendingSwap.request.to === "ARK") return this.waitAndClaimArk(pendingSwap);
4798
+ if (pendingSwap.request.to === "BTC") return this.waitAndClaimBtc(pendingSwap);
4883
4799
  throw new SwapError({
4884
4800
  message: `Unsupported swap destination: ${pendingSwap.request.to}`
4885
4801
  });
@@ -4894,11 +4810,9 @@ var ArkadeSwaps = class _ArkadeSwaps {
4894
4810
  */
4895
4811
  async createChainSwap(args) {
4896
4812
  const { to, from, receiverLockAmount, senderLockAmount, toAddress } = args;
4897
- if (!toAddress)
4898
- throw new SwapError({ message: "Destination address is required" });
4813
+ if (!toAddress) throw new SwapError({ message: "Destination address is required" });
4899
4814
  const feeSatsPerByte = args.feeSatsPerByte ?? 1;
4900
- if (feeSatsPerByte <= 0)
4901
- throw new SwapError({ message: "Invalid feeSatsPerByte" });
4815
+ if (feeSatsPerByte <= 0) throw new SwapError({ message: "Invalid feeSatsPerByte" });
4902
4816
  let amount, serverLockAmount, userLockAmount;
4903
4817
  if (receiverLockAmount) {
4904
4818
  amount = receiverLockAmount;
@@ -4913,8 +4827,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
4913
4827
  }
4914
4828
  const preimage = (0, import_utils3.randomBytes)(32);
4915
4829
  const preimageHash = import_base9.hex.encode((0, import_sha23.sha256)(preimage));
4916
- if (!preimageHash)
4917
- throw new SwapError({ message: "Failed to get preimage hash" });
4830
+ if (!preimageHash) throw new SwapError({ message: "Failed to get preimage hash" });
4918
4831
  const ephemeralKey = import_secp256k13.secp256k1.utils.randomSecretKey();
4919
4832
  const refundPublicKey = to === "ARK" ? import_base9.hex.encode(import_secp256k13.secp256k1.getPublicKey(ephemeralKey)) : import_base9.hex.encode(await this.wallet.identity.compressedPublicKey());
4920
4833
  if (!refundPublicKey)
@@ -4963,30 +4876,20 @@ var ArkadeSwaps = class _ArkadeSwaps {
4963
4876
  const { to, from, swap, arkInfo } = args;
4964
4877
  if (from === "ARK") {
4965
4878
  if (!swap.response.lockupDetails.serverPublicKey)
4966
- throw new Error(
4967
- `Swap ${swap.id}: missing serverPublicKey in lockup details`
4968
- );
4879
+ throw new Error(`Swap ${swap.id}: missing serverPublicKey in lockup details`);
4969
4880
  if (!swap.response.lockupDetails.timeouts)
4970
- throw new Error(
4971
- `Swap ${swap.id}: missing timeouts in lockup details`
4972
- );
4881
+ throw new Error(`Swap ${swap.id}: missing timeouts in lockup details`);
4973
4882
  }
4974
4883
  if (to === "ARK") {
4975
4884
  if (!swap.response.claimDetails.serverPublicKey)
4976
- throw new Error(
4977
- `Swap ${swap.id}: missing serverPublicKey in claim details`
4978
- );
4885
+ throw new Error(`Swap ${swap.id}: missing serverPublicKey in claim details`);
4979
4886
  if (!swap.response.claimDetails.timeouts)
4980
- throw new Error(
4981
- `Swap ${swap.id}: missing timeouts in claim details`
4982
- );
4887
+ throw new Error(`Swap ${swap.id}: missing timeouts in claim details`);
4983
4888
  }
4984
4889
  const lockupAddress = to === "ARK" ? swap.response.claimDetails.lockupAddress : swap.response.lockupDetails.lockupAddress;
4985
4890
  const receiverPubkey = to === "ARK" ? swap.request.claimPublicKey : swap.response.lockupDetails.serverPublicKey;
4986
4891
  const senderPubkey = to === "ARK" ? swap.response.claimDetails.serverPublicKey : swap.request.refundPublicKey;
4987
- const serverPubkey = import_base9.hex.encode(
4988
- normalizeToXOnlyKey(arkInfo.signerPubkey, "server")
4989
- );
4892
+ const serverPubkey = import_base9.hex.encode(normalizeToXOnlyKey(arkInfo.signerPubkey, "server"));
4990
4893
  const vhtlcTimeouts = to === "ARK" ? swap.response.claimDetails.timeouts : swap.response.lockupDetails.timeouts;
4991
4894
  const { vhtlcAddress } = this.createVHTLCScript({
4992
4895
  network: arkInfo.network,
@@ -5004,15 +4907,122 @@ var ArkadeSwaps = class _ArkadeSwaps {
5004
4907
  return true;
5005
4908
  }
5006
4909
  /**
5007
- * Renegotiates the quote for an existing swap.
4910
+ * Renegotiates the quote for an existing chain swap. Convenience wrapper
4911
+ * over `getSwapQuote` + `acceptSwapQuote` with a safety floor.
4912
+ *
4913
+ * The floor is resolved in order:
4914
+ * 1. `options.minAcceptableAmount` if provided.
4915
+ * 2. The original `response.claimDetails.amount` of the stored
4916
+ * pending swap (Boltz-confirmed server-lock amount at creation).
4917
+ * 3. Otherwise throws `QuoteRejectedError({ reason: "no_baseline" })`.
4918
+ *
4919
+ * `options.maxSlippageBps` (default 0) relaxes the floor by basis points.
4920
+ * Quotes ≤ 0 are always rejected. On rejection the acceptance is NOT
4921
+ * posted to Boltz.
4922
+ *
4923
+ * Prefer `getSwapQuote` / `acceptSwapQuote` for callers that want to
4924
+ * inspect the quote before committing.
4925
+ *
5008
4926
  * @param swapId - The ID of the swap.
4927
+ * @param options - Optional floor and slippage configuration.
5009
4928
  * @returns The accepted quote amount.
4929
+ * @throws QuoteRejectedError if the quote is non-positive, below the
4930
+ * effective floor, or no baseline is available.
4931
+ */
4932
+ async quoteSwap(swapId, options) {
4933
+ const effectiveFloor = await this.resolveEffectiveFloor(swapId, options);
4934
+ const amount = await this.getSwapQuote(swapId);
4935
+ this.validateQuote(amount, effectiveFloor);
4936
+ await this.swapProvider.postChainQuote(swapId, { amount });
4937
+ return amount;
4938
+ }
4939
+ /**
4940
+ * Fetches a renegotiated quote from Boltz without accepting it.
4941
+ * Pair with `acceptSwapQuote` to commit a specific value.
5010
4942
  */
5011
- async quoteSwap(swapId) {
4943
+ async getSwapQuote(swapId) {
5012
4944
  const { amount } = await this.swapProvider.getChainQuote(swapId);
4945
+ return amount;
4946
+ }
4947
+ /**
4948
+ * Accepts a quote amount for an existing chain swap, after validating it
4949
+ * against the configured floor. See `quoteSwap` for floor-resolution rules.
4950
+ *
4951
+ * @throws QuoteRejectedError if `amount` ≤ 0, below the effective floor,
4952
+ * or no baseline is available.
4953
+ */
4954
+ async acceptSwapQuote(swapId, amount, options) {
4955
+ const effectiveFloor = await this.resolveEffectiveFloor(swapId, options);
4956
+ this.validateQuote(amount, effectiveFloor);
5013
4957
  await this.swapProvider.postChainQuote(swapId, { amount });
5014
4958
  return amount;
5015
4959
  }
4960
+ async resolveEffectiveFloor(swapId, options) {
4961
+ this.validateQuoteOptions(options);
4962
+ const floor = await this.resolveQuoteFloor(swapId, options);
4963
+ const slippageBps = options?.maxSlippageBps ?? 0;
4964
+ const effectiveFloor = Math.floor(floor - floor * slippageBps / 1e4);
4965
+ if (effectiveFloor < 1) {
4966
+ throw new TypeError(
4967
+ `Invalid quote configuration: maxSlippageBps=${slippageBps} reduces floor ${floor} below 1 sat`
4968
+ );
4969
+ }
4970
+ return effectiveFloor;
4971
+ }
4972
+ async resolveQuoteFloor(swapId, options) {
4973
+ if (options?.minAcceptableAmount !== void 0) {
4974
+ return options.minAcceptableAmount;
4975
+ }
4976
+ const swaps = await this.swapRepository.getAllSwaps({
4977
+ id: swapId,
4978
+ type: "chain"
4979
+ });
4980
+ const stored = swaps[0];
4981
+ const amount = stored?.response?.claimDetails?.amount;
4982
+ if (typeof amount !== "number") {
4983
+ throw new QuoteRejectedError({ reason: "no_baseline" });
4984
+ }
4985
+ return amount;
4986
+ }
4987
+ validateQuoteOptions(options) {
4988
+ if (options?.minAcceptableAmount !== void 0) {
4989
+ const v = options.minAcceptableAmount;
4990
+ if (!Number.isInteger(v) || v <= 0) {
4991
+ throw new TypeError(
4992
+ `Invalid minAcceptableAmount: ${v} \u2014 must be a positive integer`
4993
+ );
4994
+ }
4995
+ }
4996
+ if (options?.maxSlippageBps !== void 0) {
4997
+ const v = options.maxSlippageBps;
4998
+ if (!Number.isInteger(v) || v < 0 || v > 1e4) {
4999
+ throw new TypeError(
5000
+ `Invalid maxSlippageBps: ${v} \u2014 must be an integer in [0, 10000]`
5001
+ );
5002
+ }
5003
+ }
5004
+ }
5005
+ validateQuote(amount, effectiveFloor) {
5006
+ if (!Number.isSafeInteger(amount)) {
5007
+ throw new QuoteRejectedError({
5008
+ reason: "non_safe_integer",
5009
+ quotedAmount: amount
5010
+ });
5011
+ }
5012
+ if (amount <= 0) {
5013
+ throw new QuoteRejectedError({
5014
+ reason: "non_positive",
5015
+ quotedAmount: amount
5016
+ });
5017
+ }
5018
+ if (amount < effectiveFloor) {
5019
+ throw new QuoteRejectedError({
5020
+ reason: "below_floor",
5021
+ quotedAmount: amount,
5022
+ floor: effectiveFloor
5023
+ });
5024
+ }
5025
+ }
5016
5026
  // =========================================================================
5017
5027
  // Shared utilities
5018
5028
  // =========================================================================
@@ -5026,14 +5036,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
5026
5036
  * @returns The commitment transaction ID.
5027
5037
  */
5028
5038
  async joinBatch(identity, input, output, arkInfo, isRecoverable2 = true) {
5029
- return joinBatch(
5030
- this.arkProvider,
5031
- identity,
5032
- input,
5033
- output,
5034
- arkInfo,
5035
- isRecoverable2
5036
- );
5039
+ return joinBatch(this.arkProvider, identity, input, output, arkInfo, isRecoverable2);
5037
5040
  }
5038
5041
  /**
5039
5042
  * Creates a VHTLC script for the swap.
@@ -5071,9 +5074,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
5071
5074
  async getPendingSubmarineSwaps() {
5072
5075
  const swaps = await this.getPendingSubmarineSwapsFromStorage();
5073
5076
  if (!swaps) return [];
5074
- return swaps.filter(
5075
- (swap) => swap.status === "invoice.set"
5076
- );
5077
+ return swaps.filter((swap) => swap.status === "invoice.set");
5077
5078
  }
5078
5079
  /**
5079
5080
  * Returns pending reverse swaps (those with status `swap.created`).
@@ -5081,9 +5082,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
5081
5082
  async getPendingReverseSwaps() {
5082
5083
  const swaps = await this.getPendingReverseSwapsFromStorage();
5083
5084
  if (!swaps) return [];
5084
- return swaps.filter(
5085
- (swap) => swap.status === "swap.created"
5086
- );
5085
+ return swaps.filter((swap) => swap.status === "swap.created");
5087
5086
  }
5088
5087
  /**
5089
5088
  * Returns pending chain swaps (those with status `swap.created`).
@@ -5122,10 +5121,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
5122
5121
  this.savePendingReverseSwap.bind(this)
5123
5122
  )
5124
5123
  ).catch((error) => {
5125
- logger.error(
5126
- `Failed to refresh swap status for ${swap.id}:`,
5127
- error
5128
- );
5124
+ logger.error(`Failed to refresh swap status for ${swap.id}:`, error);
5129
5125
  })
5130
5126
  );
5131
5127
  }
@@ -5139,23 +5135,15 @@ var ArkadeSwaps = class _ArkadeSwaps {
5139
5135
  this.savePendingSubmarineSwap.bind(this)
5140
5136
  )
5141
5137
  ).catch((error) => {
5142
- logger.error(
5143
- `Failed to refresh swap status for ${swap.id}:`,
5144
- error
5145
- );
5138
+ logger.error(`Failed to refresh swap status for ${swap.id}:`, error);
5146
5139
  })
5147
5140
  );
5148
5141
  }
5149
5142
  for (const swap of await this.getPendingChainSwapsFromStorage()) {
5150
5143
  if (isChainFinalStatus(swap.status)) continue;
5151
5144
  promises.push(
5152
- this.getSwapStatus(swap.id).then(
5153
- ({ status }) => this.savePendingChainSwap({ ...swap, status })
5154
- ).catch((error) => {
5155
- logger.error(
5156
- `Failed to refresh swap status for ${swap.id}:`,
5157
- error
5158
- );
5145
+ this.getSwapStatus(swap.id).then(({ status }) => this.savePendingChainSwap({ ...swap, status })).catch((error) => {
5146
+ logger.error(`Failed to refresh swap status for ${swap.id}:`, error);
5159
5147
  })
5160
5148
  );
5161
5149
  }
@@ -5172,9 +5160,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
5172
5160
  * display/monitoring and are not automatically wired into the SwapManager.
5173
5161
  */
5174
5162
  async restoreSwaps(boltzFees) {
5175
- const publicKey = import_base9.hex.encode(
5176
- await this.wallet.identity.compressedPublicKey()
5177
- );
5163
+ const publicKey = import_base9.hex.encode(await this.wallet.identity.compressedPublicKey());
5178
5164
  if (!publicKey) throw new Error("Failed to get public key from wallet");
5179
5165
  const fees = boltzFees ?? await this.swapProvider.getFees();
5180
5166
  const chainSwaps = [];
@@ -5226,25 +5212,14 @@ var ArkadeSwaps = class _ArkadeSwaps {
5226
5212
  preimage: ""
5227
5213
  });
5228
5214
  } else if (isRestoredSubmarineSwap(swap)) {
5229
- const {
5230
- amount,
5231
- lockupAddress,
5232
- serverPublicKey,
5233
- tree,
5234
- timeoutBlockHeights
5235
- } = swap.refundDetails;
5215
+ const { amount, lockupAddress, serverPublicKey, tree, timeoutBlockHeights } = swap.refundDetails;
5236
5216
  let preimage = "";
5237
5217
  if (!isSubmarineFinalStatus(status)) {
5238
5218
  try {
5239
- const data = await this.swapProvider.getSwapPreimage(
5240
- swap.id
5241
- );
5219
+ const data = await this.swapProvider.getSwapPreimage(swap.id);
5242
5220
  preimage = data.preimage;
5243
5221
  } catch (error) {
5244
- logger.warn(
5245
- `Failed to restore preimage for submarine swap ${id}`,
5246
- error
5247
- );
5222
+ logger.warn(`Failed to restore preimage for submarine swap ${id}`, error);
5248
5223
  }
5249
5224
  }
5250
5225
  submarineSwaps.push({
@@ -5282,12 +5257,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
5282
5257
  } else if (isRestoredChainSwap(swap)) {
5283
5258
  const refundDetails = swap.refundDetails;
5284
5259
  if (!refundDetails) continue;
5285
- const {
5286
- amount,
5287
- lockupAddress,
5288
- serverPublicKey,
5289
- timeoutBlockHeight
5290
- } = refundDetails;
5260
+ const { amount, lockupAddress, serverPublicKey, timeoutBlockHeight } = refundDetails;
5291
5261
  chainSwaps.push({
5292
5262
  id,
5293
5263
  type: "chain",
@@ -5337,6 +5307,12 @@ var ArkadeSwaps = class _ArkadeSwaps {
5337
5307
 
5338
5308
  // src/serviceWorker/arkade-swaps-message-handler.ts
5339
5309
  var import_sdk9 = require("@arkade-os/sdk");
5310
+ function toQuoteTransportError(error) {
5311
+ if (error instanceof QuoteRejectedError) {
5312
+ return error.toTransportError();
5313
+ }
5314
+ return error;
5315
+ }
5340
5316
  var DEFAULT_MESSAGE_TAG = "ARKADE_SWAPS_UPDATER";
5341
5317
  var HANDLER_NOT_INITIALIZED = "ArkadeSwaps handler not initialized";
5342
5318
  var HandlerNotInitializedError = class extends Error {
@@ -5452,9 +5428,7 @@ var ArkadeSwapsMessageHandler = class _ArkadeSwapsMessageHandler {
5452
5428
  try {
5453
5429
  switch (message.type) {
5454
5430
  case "CREATE_LIGHTNING_INVOICE": {
5455
- const res = await this.handler.createLightningInvoice(
5456
- message.payload
5457
- );
5431
+ const res = await this.handler.createLightningInvoice(message.payload);
5458
5432
  return this.tagged({
5459
5433
  id,
5460
5434
  type: "LIGHTNING_INVOICE_CREATED",
@@ -5462,9 +5436,7 @@ var ArkadeSwapsMessageHandler = class _ArkadeSwapsMessageHandler {
5462
5436
  });
5463
5437
  }
5464
5438
  case "SEND_LIGHTNING_PAYMENT": {
5465
- const res = await this.handler.sendLightningPayment(
5466
- message.payload
5467
- );
5439
+ const res = await this.handler.sendLightningPayment(message.payload);
5468
5440
  return this.tagged({
5469
5441
  id,
5470
5442
  type: "LIGHTNING_PAYMENT_SENT",
@@ -5472,9 +5444,7 @@ var ArkadeSwapsMessageHandler = class _ArkadeSwapsMessageHandler {
5472
5444
  });
5473
5445
  }
5474
5446
  case "CREATE_SUBMARINE_SWAP": {
5475
- const res = await this.handler.createSubmarineSwap(
5476
- message.payload
5477
- );
5447
+ const res = await this.handler.createSubmarineSwap(message.payload);
5478
5448
  return this.tagged({
5479
5449
  id,
5480
5450
  type: "SUBMARINE_SWAP_CREATED",
@@ -5482,9 +5452,7 @@ var ArkadeSwapsMessageHandler = class _ArkadeSwapsMessageHandler {
5482
5452
  });
5483
5453
  }
5484
5454
  case "CREATE_REVERSE_SWAP": {
5485
- const res = await this.handler.createReverseSwap(
5486
- message.payload
5487
- );
5455
+ const res = await this.handler.createReverseSwap(message.payload);
5488
5456
  return this.tagged({
5489
5457
  id,
5490
5458
  type: "REVERSE_SWAP_CREATED",
@@ -5495,9 +5463,7 @@ var ArkadeSwapsMessageHandler = class _ArkadeSwapsMessageHandler {
5495
5463
  await this.handler.claimVHTLC(message.payload);
5496
5464
  return this.tagged({ id, type: "VHTLC_CLAIMED" });
5497
5465
  case "REFUND_VHTLC": {
5498
- const outcome = await this.handler.refundVHTLC(
5499
- message.payload
5500
- );
5466
+ const outcome = await this.handler.refundVHTLC(message.payload);
5501
5467
  return this.tagged({
5502
5468
  id,
5503
5469
  type: "VHTLC_REFUNDED",
@@ -5505,9 +5471,7 @@ var ArkadeSwapsMessageHandler = class _ArkadeSwapsMessageHandler {
5505
5471
  });
5506
5472
  }
5507
5473
  case "INSPECT_SUBMARINE_RECOVERY": {
5508
- const info = await this.handler.inspectSubmarineRecovery(
5509
- message.payload
5510
- );
5474
+ const info = await this.handler.inspectSubmarineRecovery(message.payload);
5511
5475
  return this.tagged({
5512
5476
  id,
5513
5477
  type: "SUBMARINE_RECOVERY_INSPECTED",
@@ -5523,9 +5487,7 @@ var ArkadeSwapsMessageHandler = class _ArkadeSwapsMessageHandler {
5523
5487
  });
5524
5488
  }
5525
5489
  case "RECOVER_SUBMARINE_FUNDS": {
5526
- const outcome = await this.handler.recoverSubmarineFunds(
5527
- message.payload
5528
- );
5490
+ const outcome = await this.handler.recoverSubmarineFunds(message.payload);
5529
5491
  return this.tagged({
5530
5492
  id,
5531
5493
  type: "SUBMARINE_FUNDS_RECOVERED",
@@ -5533,9 +5495,7 @@ var ArkadeSwapsMessageHandler = class _ArkadeSwapsMessageHandler {
5533
5495
  });
5534
5496
  }
5535
5497
  case "RECOVER_ALL_SUBMARINE_FUNDS": {
5536
- const results = await this.handler.recoverAllSubmarineFunds(
5537
- message.payload
5538
- );
5498
+ const results = await this.handler.recoverAllSubmarineFunds(message.payload);
5539
5499
  return this.tagged({
5540
5500
  id,
5541
5501
  type: "ALL_SUBMARINE_FUNDS_RECOVERED",
@@ -5543,9 +5503,7 @@ var ArkadeSwapsMessageHandler = class _ArkadeSwapsMessageHandler {
5543
5503
  });
5544
5504
  }
5545
5505
  case "WAIT_AND_CLAIM": {
5546
- const res = await this.handler.waitAndClaim(
5547
- message.payload
5548
- );
5506
+ const res = await this.handler.waitAndClaim(message.payload);
5549
5507
  return this.tagged({
5550
5508
  id,
5551
5509
  type: "WAIT_AND_CLAIMED",
@@ -5553,9 +5511,7 @@ var ArkadeSwapsMessageHandler = class _ArkadeSwapsMessageHandler {
5553
5511
  });
5554
5512
  }
5555
5513
  case "WAIT_FOR_SWAP_SETTLEMENT": {
5556
- const res = await this.handler.waitForSwapSettlement(
5557
- message.payload
5558
- );
5514
+ const res = await this.handler.waitForSwapSettlement(message.payload);
5559
5515
  return this.tagged({
5560
5516
  id,
5561
5517
  type: "SWAP_SETTLED",
@@ -5563,9 +5519,7 @@ var ArkadeSwapsMessageHandler = class _ArkadeSwapsMessageHandler {
5563
5519
  });
5564
5520
  }
5565
5521
  case "RESTORE_SWAPS": {
5566
- const res = await this.handler.restoreSwaps(
5567
- message.payload
5568
- );
5522
+ const res = await this.handler.restoreSwaps(message.payload);
5569
5523
  return this.tagged({
5570
5524
  id,
5571
5525
  type: "SWAPS_RESTORED",
@@ -5595,23 +5549,15 @@ var ArkadeSwapsMessageHandler = class _ArkadeSwapsMessageHandler {
5595
5549
  });
5596
5550
  }
5597
5551
  case "GET_FEES": {
5598
- const res = message.payload ? await this.handler.getFees(
5599
- message.payload.from,
5600
- message.payload.to
5601
- ) : await this.handler.getFees();
5552
+ const res = message.payload ? await this.handler.getFees(message.payload.from, message.payload.to) : await this.handler.getFees();
5602
5553
  return this.tagged({ id, type: "FEES", payload: res });
5603
5554
  }
5604
5555
  case "GET_LIMITS": {
5605
- const res = message.payload ? await this.handler.getLimits(
5606
- message.payload.from,
5607
- message.payload.to
5608
- ) : await this.handler.getLimits();
5556
+ const res = message.payload ? await this.handler.getLimits(message.payload.from, message.payload.to) : await this.handler.getLimits();
5609
5557
  return this.tagged({ id, type: "LIMITS", payload: res });
5610
5558
  }
5611
5559
  case "GET_SWAP_STATUS": {
5612
- const res = await this.handler.getSwapStatus(
5613
- message.payload.swapId
5614
- );
5560
+ const res = await this.handler.getSwapStatus(message.payload.swapId);
5615
5561
  return this.tagged({
5616
5562
  id,
5617
5563
  type: "SWAP_STATUS",
@@ -5670,9 +5616,7 @@ var ArkadeSwapsMessageHandler = class _ArkadeSwapsMessageHandler {
5670
5616
  });
5671
5617
  }
5672
5618
  case "CREATE_CHAIN_SWAP": {
5673
- const res = await this.handler.createChainSwap(
5674
- message.payload
5675
- );
5619
+ const res = await this.handler.createChainSwap(message.payload);
5676
5620
  return this.tagged({
5677
5621
  id,
5678
5622
  type: "CHAIN_SWAP_CREATED",
@@ -5680,9 +5624,7 @@ var ArkadeSwapsMessageHandler = class _ArkadeSwapsMessageHandler {
5680
5624
  });
5681
5625
  }
5682
5626
  case "WAIT_AND_CLAIM_CHAIN": {
5683
- const res = await this.handler.waitAndClaimChain(
5684
- message.payload
5685
- );
5627
+ const res = await this.handler.waitAndClaimChain(message.payload);
5686
5628
  return this.tagged({
5687
5629
  id,
5688
5630
  type: "CHAIN_CLAIMED",
@@ -5690,9 +5632,7 @@ var ArkadeSwapsMessageHandler = class _ArkadeSwapsMessageHandler {
5690
5632
  });
5691
5633
  }
5692
5634
  case "WAIT_AND_CLAIM_ARK": {
5693
- const res = await this.handler.waitAndClaimArk(
5694
- message.payload
5695
- );
5635
+ const res = await this.handler.waitAndClaimArk(message.payload);
5696
5636
  return this.tagged({
5697
5637
  id,
5698
5638
  type: "ARK_CLAIMED",
@@ -5700,9 +5640,7 @@ var ArkadeSwapsMessageHandler = class _ArkadeSwapsMessageHandler {
5700
5640
  });
5701
5641
  }
5702
5642
  case "WAIT_AND_CLAIM_BTC": {
5703
- const res = await this.handler.waitAndClaimBtc(
5704
- message.payload
5705
- );
5643
+ const res = await this.handler.waitAndClaimBtc(message.payload);
5706
5644
  return this.tagged({
5707
5645
  id,
5708
5646
  type: "BTC_CLAIMED",
@@ -5715,13 +5653,16 @@ var ArkadeSwapsMessageHandler = class _ArkadeSwapsMessageHandler {
5715
5653
  case "CLAIM_BTC":
5716
5654
  await this.handler.claimBtc(message.payload);
5717
5655
  return this.tagged({ id, type: "BTC_CLAIM_EXECUTED" });
5718
- case "REFUND_ARK":
5719
- await this.handler.refundArk(message.payload);
5720
- return this.tagged({ id, type: "ARK_REFUND_EXECUTED" });
5656
+ case "REFUND_ARK": {
5657
+ const outcome = await this.handler.refundArk(message.payload);
5658
+ return this.tagged({
5659
+ id,
5660
+ type: "ARK_REFUND_EXECUTED",
5661
+ payload: outcome
5662
+ });
5663
+ }
5721
5664
  case "SIGN_SERVER_CLAIM":
5722
- await this.handler.signCooperativeClaimForServer(
5723
- message.payload
5724
- );
5665
+ await this.handler.signCooperativeClaimForServer(message.payload);
5725
5666
  return this.tagged({ id, type: "SERVER_CLAIM_SIGNED" });
5726
5667
  case "VERIFY_CHAIN_SWAP": {
5727
5668
  const verified = await this.handler.verifyChainSwap({
@@ -5734,14 +5675,47 @@ var ArkadeSwapsMessageHandler = class _ArkadeSwapsMessageHandler {
5734
5675
  });
5735
5676
  }
5736
5677
  case "QUOTE_SWAP": {
5737
- const amount = await this.handler.quoteSwap(
5738
- message.payload.swapId
5739
- );
5740
- return this.tagged({
5741
- id,
5742
- type: "SWAP_QUOTED",
5743
- payload: { amount }
5744
- });
5678
+ try {
5679
+ const amount = await this.handler.quoteSwap(
5680
+ message.payload.swapId,
5681
+ message.payload.options
5682
+ );
5683
+ return this.tagged({
5684
+ id,
5685
+ type: "SWAP_QUOTED",
5686
+ payload: { amount }
5687
+ });
5688
+ } catch (e) {
5689
+ throw toQuoteTransportError(e);
5690
+ }
5691
+ }
5692
+ case "GET_SWAP_QUOTE": {
5693
+ try {
5694
+ const amount = await this.handler.getSwapQuote(message.payload.swapId);
5695
+ return this.tagged({
5696
+ id,
5697
+ type: "SWAP_QUOTE_RETRIEVED",
5698
+ payload: { amount }
5699
+ });
5700
+ } catch (e) {
5701
+ throw toQuoteTransportError(e);
5702
+ }
5703
+ }
5704
+ case "ACCEPT_SWAP_QUOTE": {
5705
+ try {
5706
+ const amount = await this.handler.acceptSwapQuote(
5707
+ message.payload.swapId,
5708
+ message.payload.amount,
5709
+ message.payload.options
5710
+ );
5711
+ return this.tagged({
5712
+ id,
5713
+ type: "SWAP_QUOTE_ACCEPTED",
5714
+ payload: { amount }
5715
+ });
5716
+ } catch (e) {
5717
+ throw toQuoteTransportError(e);
5718
+ }
5745
5719
  }
5746
5720
  /* --- SwapManager methods --- */
5747
5721
  case "SM-START": {
@@ -5757,9 +5731,7 @@ var ArkadeSwapsMessageHandler = class _ArkadeSwapsMessageHandler {
5757
5731
  return this.tagged({ id, type: "SM-SWAP_ADDED" });
5758
5732
  }
5759
5733
  case "SM-REMOVE_SWAP": {
5760
- await this.getSwapManagerOrThrow().removeSwap(
5761
- message.payload.swapId
5762
- );
5734
+ await this.getSwapManagerOrThrow().removeSwap(message.payload.swapId);
5763
5735
  return this.tagged({ id, type: "SM-SWAP_REMOVED" });
5764
5736
  }
5765
5737
  case "SM-GET_PENDING_SWAPS": {
@@ -5771,9 +5743,7 @@ var ArkadeSwapsMessageHandler = class _ArkadeSwapsMessageHandler {
5771
5743
  });
5772
5744
  }
5773
5745
  case "SM-HAS_SWAP": {
5774
- const has = await this.getSwapManagerOrThrow().hasSwap(
5775
- message.payload.swapId
5776
- );
5746
+ const has = await this.getSwapManagerOrThrow().hasSwap(message.payload.swapId);
5777
5747
  return this.tagged({
5778
5748
  id,
5779
5749
  type: "SM-HAS_SWAP_RESULT",
@@ -5893,6 +5863,11 @@ function isMessageBusNotInitializedError(error) {
5893
5863
  function isHandlerNotInitializedError(error) {
5894
5864
  return error instanceof Error && error.message.includes(HANDLER_NOT_INITIALIZED);
5895
5865
  }
5866
+ function rethrowIfQuoteRejected(error) {
5867
+ if (error instanceof QuoteRejectedError) throw error;
5868
+ const rebuilt = QuoteRejectedError.fromTransportError(error);
5869
+ if (rebuilt) throw rebuilt;
5870
+ }
5896
5871
  var DEFAULT_MESSAGE_TIMEOUT_MS = 3e4;
5897
5872
  var NO_MESSAGE_TIMEOUT_MS = 0;
5898
5873
  var DEDUPABLE_REQUEST_TYPES = /* @__PURE__ */ new Set([
@@ -5949,7 +5924,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
5949
5924
  };
5950
5925
  const initMessage = {
5951
5926
  tag: messageTag,
5952
- id: getRandomId(),
5927
+ id: (0, import_sdk10.getRandomId)(),
5953
5928
  type: "INIT_ARKADE_SWAPS",
5954
5929
  payload: initPayload
5955
5930
  };
@@ -5962,7 +5937,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
5962
5937
  throw new Error("SwapManager is not enabled.");
5963
5938
  }
5964
5939
  await this.sendMessage({
5965
- id: getRandomId(),
5940
+ id: (0, import_sdk10.getRandomId)(),
5966
5941
  tag: this.messageTag,
5967
5942
  type: "SM-START"
5968
5943
  });
@@ -5970,7 +5945,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
5970
5945
  async stopSwapManager() {
5971
5946
  if (!this.withSwapManager) return;
5972
5947
  await this.sendMessage({
5973
- id: getRandomId(),
5948
+ id: (0, import_sdk10.getRandomId)(),
5974
5949
  tag: this.messageTag,
5975
5950
  type: "SM-STOP"
5976
5951
  });
@@ -5985,21 +5960,21 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
5985
5960
  const proxy = {
5986
5961
  start: async () => {
5987
5962
  await send({
5988
- id: getRandomId(),
5963
+ id: (0, import_sdk10.getRandomId)(),
5989
5964
  tag,
5990
5965
  type: "SM-START"
5991
5966
  });
5992
5967
  },
5993
5968
  stop: async () => {
5994
5969
  await send({
5995
- id: getRandomId(),
5970
+ id: (0, import_sdk10.getRandomId)(),
5996
5971
  tag,
5997
5972
  type: "SM-STOP"
5998
5973
  });
5999
5974
  },
6000
5975
  addSwap: async (swap) => {
6001
5976
  await send({
6002
- id: getRandomId(),
5977
+ id: (0, import_sdk10.getRandomId)(),
6003
5978
  tag,
6004
5979
  type: "SM-ADD_SWAP",
6005
5980
  payload: swap
@@ -6007,7 +5982,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6007
5982
  },
6008
5983
  removeSwap: async (swapId) => {
6009
5984
  await send({
6010
- id: getRandomId(),
5985
+ id: (0, import_sdk10.getRandomId)(),
6011
5986
  tag,
6012
5987
  type: "SM-REMOVE_SWAP",
6013
5988
  payload: { swapId }
@@ -6015,7 +5990,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6015
5990
  },
6016
5991
  getPendingSwaps: async () => {
6017
5992
  const res = await send({
6018
- id: getRandomId(),
5993
+ id: (0, import_sdk10.getRandomId)(),
6019
5994
  tag,
6020
5995
  type: "SM-GET_PENDING_SWAPS"
6021
5996
  });
@@ -6023,7 +5998,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6023
5998
  },
6024
5999
  hasSwap: async (swapId) => {
6025
6000
  const res = await send({
6026
- id: getRandomId(),
6001
+ id: (0, import_sdk10.getRandomId)(),
6027
6002
  tag,
6028
6003
  type: "SM-HAS_SWAP",
6029
6004
  payload: { swapId }
@@ -6032,7 +6007,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6032
6007
  },
6033
6008
  isProcessing: async (swapId) => {
6034
6009
  const res = await send({
6035
- id: getRandomId(),
6010
+ id: (0, import_sdk10.getRandomId)(),
6036
6011
  tag,
6037
6012
  type: "SM-IS_PROCESSING",
6038
6013
  payload: { swapId }
@@ -6041,7 +6016,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6041
6016
  },
6042
6017
  getStats: async () => {
6043
6018
  const res = await send({
6044
- id: getRandomId(),
6019
+ id: (0, import_sdk10.getRandomId)(),
6045
6020
  tag,
6046
6021
  type: "SM-GET_STATS"
6047
6022
  });
@@ -6049,7 +6024,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6049
6024
  },
6050
6025
  waitForSwapCompletion: async (swapId) => {
6051
6026
  const res = await send({
6052
- id: getRandomId(),
6027
+ id: (0, import_sdk10.getRandomId)(),
6053
6028
  tag,
6054
6029
  type: "SM-WAIT_FOR_COMPLETION",
6055
6030
  payload: { swapId }
@@ -6113,7 +6088,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6113
6088
  async createLightningInvoice(args) {
6114
6089
  try {
6115
6090
  const res = await this.sendMessage({
6116
- id: getRandomId(),
6091
+ id: (0, import_sdk10.getRandomId)(),
6117
6092
  tag: this.messageTag,
6118
6093
  type: "CREATE_LIGHTNING_INVOICE",
6119
6094
  payload: args
@@ -6126,7 +6101,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6126
6101
  async sendLightningPayment(args) {
6127
6102
  try {
6128
6103
  const res = await this.sendMessage({
6129
- id: getRandomId(),
6104
+ id: (0, import_sdk10.getRandomId)(),
6130
6105
  tag: this.messageTag,
6131
6106
  type: "SEND_LIGHTNING_PAYMENT",
6132
6107
  payload: args
@@ -6139,7 +6114,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6139
6114
  async createSubmarineSwap(args) {
6140
6115
  try {
6141
6116
  const res = await this.sendMessage({
6142
- id: getRandomId(),
6117
+ id: (0, import_sdk10.getRandomId)(),
6143
6118
  tag: this.messageTag,
6144
6119
  type: "CREATE_SUBMARINE_SWAP",
6145
6120
  payload: args
@@ -6152,7 +6127,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6152
6127
  async createReverseSwap(args) {
6153
6128
  try {
6154
6129
  const res = await this.sendMessage({
6155
- id: getRandomId(),
6130
+ id: (0, import_sdk10.getRandomId)(),
6156
6131
  tag: this.messageTag,
6157
6132
  type: "CREATE_REVERSE_SWAP",
6158
6133
  payload: args
@@ -6164,7 +6139,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6164
6139
  }
6165
6140
  async claimVHTLC(pendingSwap) {
6166
6141
  await this.sendMessage({
6167
- id: getRandomId(),
6142
+ id: (0, import_sdk10.getRandomId)(),
6168
6143
  tag: this.messageTag,
6169
6144
  type: "CLAIM_VHTLC",
6170
6145
  payload: pendingSwap
@@ -6172,7 +6147,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6172
6147
  }
6173
6148
  async refundVHTLC(pendingSwap) {
6174
6149
  const res = await this.sendMessage({
6175
- id: getRandomId(),
6150
+ id: (0, import_sdk10.getRandomId)(),
6176
6151
  tag: this.messageTag,
6177
6152
  type: "REFUND_VHTLC",
6178
6153
  payload: pendingSwap
@@ -6181,7 +6156,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6181
6156
  }
6182
6157
  async inspectSubmarineRecovery(swap) {
6183
6158
  const res = await this.sendMessage({
6184
- id: getRandomId(),
6159
+ id: (0, import_sdk10.getRandomId)(),
6185
6160
  tag: this.messageTag,
6186
6161
  type: "INSPECT_SUBMARINE_RECOVERY",
6187
6162
  payload: swap
@@ -6190,7 +6165,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6190
6165
  }
6191
6166
  async scanRecoverableSubmarineSwaps() {
6192
6167
  const res = await this.sendMessage({
6193
- id: getRandomId(),
6168
+ id: (0, import_sdk10.getRandomId)(),
6194
6169
  tag: this.messageTag,
6195
6170
  type: "SCAN_RECOVERABLE_SUBMARINE_SWAPS"
6196
6171
  });
@@ -6198,7 +6173,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6198
6173
  }
6199
6174
  async recoverSubmarineFunds(swap) {
6200
6175
  const res = await this.sendMessage({
6201
- id: getRandomId(),
6176
+ id: (0, import_sdk10.getRandomId)(),
6202
6177
  tag: this.messageTag,
6203
6178
  type: "RECOVER_SUBMARINE_FUNDS",
6204
6179
  payload: swap
@@ -6207,7 +6182,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6207
6182
  }
6208
6183
  async recoverAllSubmarineFunds(swaps) {
6209
6184
  const res = await this.sendMessage({
6210
- id: getRandomId(),
6185
+ id: (0, import_sdk10.getRandomId)(),
6211
6186
  tag: this.messageTag,
6212
6187
  type: "RECOVER_ALL_SUBMARINE_FUNDS",
6213
6188
  payload: swaps
@@ -6217,7 +6192,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6217
6192
  async waitAndClaim(pendingSwap) {
6218
6193
  try {
6219
6194
  const res = await this.sendMessage({
6220
- id: getRandomId(),
6195
+ id: (0, import_sdk10.getRandomId)(),
6221
6196
  tag: this.messageTag,
6222
6197
  type: "WAIT_AND_CLAIM",
6223
6198
  payload: pendingSwap
@@ -6232,7 +6207,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6232
6207
  async waitForSwapSettlement(pendingSwap) {
6233
6208
  try {
6234
6209
  const res = await this.sendMessage({
6235
- id: getRandomId(),
6210
+ id: (0, import_sdk10.getRandomId)(),
6236
6211
  tag: this.messageTag,
6237
6212
  type: "WAIT_FOR_SWAP_SETTLEMENT",
6238
6213
  payload: pendingSwap
@@ -6245,7 +6220,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6245
6220
  async restoreSwaps(boltzFees) {
6246
6221
  try {
6247
6222
  const res = await this.sendMessage({
6248
- id: getRandomId(),
6223
+ id: (0, import_sdk10.getRandomId)(),
6249
6224
  tag: this.messageTag,
6250
6225
  type: "RESTORE_SWAPS",
6251
6226
  payload: boltzFees
@@ -6258,7 +6233,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6258
6233
  async arkToBtc(args) {
6259
6234
  try {
6260
6235
  const res = await this.sendMessage({
6261
- id: getRandomId(),
6236
+ id: (0, import_sdk10.getRandomId)(),
6262
6237
  tag: this.messageTag,
6263
6238
  type: "ARK_TO_BTC",
6264
6239
  payload: args
@@ -6273,7 +6248,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6273
6248
  async btcToArk(args) {
6274
6249
  try {
6275
6250
  const res = await this.sendMessage({
6276
- id: getRandomId(),
6251
+ id: (0, import_sdk10.getRandomId)(),
6277
6252
  tag: this.messageTag,
6278
6253
  type: "BTC_TO_ARK",
6279
6254
  payload: args
@@ -6288,7 +6263,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6288
6263
  async createChainSwap(args) {
6289
6264
  try {
6290
6265
  const res = await this.sendMessage({
6291
- id: getRandomId(),
6266
+ id: (0, import_sdk10.getRandomId)(),
6292
6267
  tag: this.messageTag,
6293
6268
  type: "CREATE_CHAIN_SWAP",
6294
6269
  payload: args
@@ -6301,7 +6276,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6301
6276
  async waitAndClaimChain(pendingSwap) {
6302
6277
  try {
6303
6278
  const res = await this.sendMessage({
6304
- id: getRandomId(),
6279
+ id: (0, import_sdk10.getRandomId)(),
6305
6280
  tag: this.messageTag,
6306
6281
  type: "WAIT_AND_CLAIM_CHAIN",
6307
6282
  payload: pendingSwap
@@ -6316,7 +6291,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6316
6291
  async waitAndClaimArk(pendingSwap) {
6317
6292
  try {
6318
6293
  const res = await this.sendMessage({
6319
- id: getRandomId(),
6294
+ id: (0, import_sdk10.getRandomId)(),
6320
6295
  tag: this.messageTag,
6321
6296
  type: "WAIT_AND_CLAIM_ARK",
6322
6297
  payload: pendingSwap
@@ -6331,7 +6306,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6331
6306
  async waitAndClaimBtc(pendingSwap) {
6332
6307
  try {
6333
6308
  const res = await this.sendMessage({
6334
- id: getRandomId(),
6309
+ id: (0, import_sdk10.getRandomId)(),
6335
6310
  tag: this.messageTag,
6336
6311
  type: "WAIT_AND_CLAIM_BTC",
6337
6312
  payload: pendingSwap
@@ -6345,7 +6320,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6345
6320
  }
6346
6321
  async claimArk(pendingSwap) {
6347
6322
  await this.sendMessage({
6348
- id: getRandomId(),
6323
+ id: (0, import_sdk10.getRandomId)(),
6349
6324
  tag: this.messageTag,
6350
6325
  type: "CLAIM_ARK",
6351
6326
  payload: pendingSwap
@@ -6353,23 +6328,24 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6353
6328
  }
6354
6329
  async claimBtc(pendingSwap) {
6355
6330
  await this.sendMessage({
6356
- id: getRandomId(),
6331
+ id: (0, import_sdk10.getRandomId)(),
6357
6332
  tag: this.messageTag,
6358
6333
  type: "CLAIM_BTC",
6359
6334
  payload: pendingSwap
6360
6335
  });
6361
6336
  }
6362
6337
  async refundArk(pendingSwap) {
6363
- await this.sendMessage({
6364
- id: getRandomId(),
6338
+ const res = await this.sendMessage({
6339
+ id: (0, import_sdk10.getRandomId)(),
6365
6340
  tag: this.messageTag,
6366
6341
  type: "REFUND_ARK",
6367
6342
  payload: pendingSwap
6368
6343
  });
6344
+ return res.payload;
6369
6345
  }
6370
6346
  async signCooperativeClaimForServer(pendingSwap) {
6371
6347
  await this.sendMessage({
6372
- id: getRandomId(),
6348
+ id: (0, import_sdk10.getRandomId)(),
6373
6349
  tag: this.messageTag,
6374
6350
  type: "SIGN_SERVER_CLAIM",
6375
6351
  payload: pendingSwap
@@ -6378,7 +6354,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6378
6354
  async verifyChainSwap(args) {
6379
6355
  try {
6380
6356
  const res = await this.sendMessage({
6381
- id: getRandomId(),
6357
+ id: (0, import_sdk10.getRandomId)(),
6382
6358
  tag: this.messageTag,
6383
6359
  type: "VERIFY_CHAIN_SWAP",
6384
6360
  payload: {
@@ -6393,19 +6369,48 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6393
6369
  throw new Error("Cannot verify chain swap", { cause: e });
6394
6370
  }
6395
6371
  }
6396
- async quoteSwap(swapId) {
6372
+ async quoteSwap(swapId, options) {
6397
6373
  try {
6398
6374
  const res = await this.sendMessage({
6399
- id: getRandomId(),
6375
+ id: (0, import_sdk10.getRandomId)(),
6400
6376
  tag: this.messageTag,
6401
6377
  type: "QUOTE_SWAP",
6402
- payload: { swapId }
6378
+ payload: { swapId, options }
6403
6379
  });
6404
6380
  return res.payload.amount;
6405
6381
  } catch (e) {
6382
+ rethrowIfQuoteRejected(e);
6406
6383
  throw new Error("Cannot quote swap", { cause: e });
6407
6384
  }
6408
6385
  }
6386
+ async getSwapQuote(swapId) {
6387
+ try {
6388
+ const res = await this.sendMessage({
6389
+ id: (0, import_sdk10.getRandomId)(),
6390
+ tag: this.messageTag,
6391
+ type: "GET_SWAP_QUOTE",
6392
+ payload: { swapId }
6393
+ });
6394
+ return res.payload.amount;
6395
+ } catch (e) {
6396
+ rethrowIfQuoteRejected(e);
6397
+ throw new Error("Cannot get swap quote", { cause: e });
6398
+ }
6399
+ }
6400
+ async acceptSwapQuote(swapId, amount, options) {
6401
+ try {
6402
+ const res = await this.sendMessage({
6403
+ id: (0, import_sdk10.getRandomId)(),
6404
+ tag: this.messageTag,
6405
+ type: "ACCEPT_SWAP_QUOTE",
6406
+ payload: { swapId, amount, options }
6407
+ });
6408
+ return res.payload.amount;
6409
+ } catch (e) {
6410
+ rethrowIfQuoteRejected(e);
6411
+ throw new Error("Cannot accept swap quote", { cause: e });
6412
+ }
6413
+ }
6409
6414
  enrichReverseSwapPreimage(swap, preimage) {
6410
6415
  return enrichReverseSwapPreimage(swap, preimage);
6411
6416
  }
@@ -6413,9 +6418,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6413
6418
  return enrichSubmarineSwapInvoice(swap, invoice);
6414
6419
  }
6415
6420
  createVHTLCScript(_args) {
6416
- throw new Error(
6417
- "createVHTLCScript is not supported via service worker"
6418
- );
6421
+ throw new Error("createVHTLCScript is not supported via service worker");
6419
6422
  }
6420
6423
  async joinBatch(_identity, _input, _output, _arkInfo, _isRecoverable = true) {
6421
6424
  throw new Error("joinBatch is not supported via service worker");
@@ -6426,7 +6429,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6426
6429
  }
6427
6430
  try {
6428
6431
  const res = await this.sendMessage({
6429
- id: getRandomId(),
6432
+ id: (0, import_sdk10.getRandomId)(),
6430
6433
  tag: this.messageTag,
6431
6434
  type: "GET_FEES",
6432
6435
  ...from !== void 0 && to !== void 0 ? { payload: { from, to } } : {}
@@ -6442,7 +6445,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6442
6445
  }
6443
6446
  try {
6444
6447
  const res = await this.sendMessage({
6445
- id: getRandomId(),
6448
+ id: (0, import_sdk10.getRandomId)(),
6446
6449
  tag: this.messageTag,
6447
6450
  type: "GET_LIMITS",
6448
6451
  ...from !== void 0 && to !== void 0 ? { payload: { from, to } } : {}
@@ -6455,7 +6458,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6455
6458
  async getSwapStatus(swapId) {
6456
6459
  try {
6457
6460
  const res = await this.sendMessage({
6458
- id: getRandomId(),
6461
+ id: (0, import_sdk10.getRandomId)(),
6459
6462
  tag: this.messageTag,
6460
6463
  type: "GET_SWAP_STATUS",
6461
6464
  payload: { swapId }
@@ -6468,7 +6471,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6468
6471
  async getPendingSubmarineSwaps() {
6469
6472
  try {
6470
6473
  const res = await this.sendMessage({
6471
- id: getRandomId(),
6474
+ id: (0, import_sdk10.getRandomId)(),
6472
6475
  tag: this.messageTag,
6473
6476
  type: "GET_PENDING_SUBMARINE_SWAPS"
6474
6477
  });
@@ -6482,7 +6485,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6482
6485
  async getPendingReverseSwaps() {
6483
6486
  try {
6484
6487
  const res = await this.sendMessage({
6485
- id: getRandomId(),
6488
+ id: (0, import_sdk10.getRandomId)(),
6486
6489
  tag: this.messageTag,
6487
6490
  type: "GET_PENDING_REVERSE_SWAPS"
6488
6491
  });
@@ -6494,7 +6497,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6494
6497
  async getPendingChainSwaps() {
6495
6498
  try {
6496
6499
  const res = await this.sendMessage({
6497
- id: getRandomId(),
6500
+ id: (0, import_sdk10.getRandomId)(),
6498
6501
  tag: this.messageTag,
6499
6502
  type: "GET_PENDING_CHAIN_SWAPS"
6500
6503
  });
@@ -6506,7 +6509,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6506
6509
  async getSwapHistory() {
6507
6510
  try {
6508
6511
  const res = await this.sendMessage({
6509
- id: getRandomId(),
6512
+ id: (0, import_sdk10.getRandomId)(),
6510
6513
  tag: this.messageTag,
6511
6514
  type: "GET_SWAP_HISTORY"
6512
6515
  });
@@ -6517,7 +6520,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6517
6520
  }
6518
6521
  async refreshSwapsStatus() {
6519
6522
  await this.sendMessage({
6520
- id: getRandomId(),
6523
+ id: (0, import_sdk10.getRandomId)(),
6521
6524
  tag: this.messageTag,
6522
6525
  type: "REFRESH_SWAPS_STATUS"
6523
6526
  });
@@ -6545,10 +6548,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6545
6548
  return new Promise((resolve, reject) => {
6546
6549
  const cleanup = () => {
6547
6550
  clearTimeout(timeoutId);
6548
- navigator.serviceWorker.removeEventListener(
6549
- "message",
6550
- messageHandler
6551
- );
6551
+ navigator.serviceWorker.removeEventListener("message", messageHandler);
6552
6552
  };
6553
6553
  const timeoutId = timeoutMs > 0 ? setTimeout(() => {
6554
6554
  cleanup();
@@ -6590,21 +6590,14 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6590
6590
  pingServiceWorker() {
6591
6591
  if (this.pingPromise) return this.pingPromise;
6592
6592
  this.pingPromise = new Promise((resolve, reject) => {
6593
- const pingId = getRandomId();
6593
+ const pingId = (0, import_sdk10.getRandomId)();
6594
6594
  const cleanup = () => {
6595
6595
  clearTimeout(timeoutId);
6596
- navigator.serviceWorker.removeEventListener(
6597
- "message",
6598
- onMessage
6599
- );
6596
+ navigator.serviceWorker.removeEventListener("message", onMessage);
6600
6597
  };
6601
6598
  const timeoutId = setTimeout(() => {
6602
6599
  cleanup();
6603
- reject(
6604
- new import_sdk10.ServiceWorkerTimeoutError(
6605
- "Service worker ping timed out"
6606
- )
6607
- );
6600
+ reject(new import_sdk10.ServiceWorkerTimeoutError("Service worker ping timed out"));
6608
6601
  }, 2e3);
6609
6602
  const onMessage = (event) => {
6610
6603
  if (event.data?.id === pingId && event.data?.tag === "PONG") {
@@ -6632,9 +6625,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6632
6625
  await this.reinitialize();
6633
6626
  }
6634
6627
  }
6635
- const timeoutMs = LONG_RUNNING_ARKADE_SWAPS_REQUEST_TYPES.has(
6636
- request.type
6637
- ) ? NO_MESSAGE_TIMEOUT_MS : DEFAULT_MESSAGE_TIMEOUT_MS;
6628
+ const timeoutMs = LONG_RUNNING_ARKADE_SWAPS_REQUEST_TYPES.has(request.type) ? NO_MESSAGE_TIMEOUT_MS : DEFAULT_MESSAGE_TIMEOUT_MS;
6638
6629
  const maxRetries = 2;
6639
6630
  for (let attempt = 0; ; attempt++) {
6640
6631
  try {
@@ -6657,13 +6648,10 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6657
6648
  const initMessage = {
6658
6649
  tag: this.messageTag,
6659
6650
  type: "INIT_ARKADE_SWAPS",
6660
- id: getRandomId(),
6651
+ id: (0, import_sdk10.getRandomId)(),
6661
6652
  payload: this.initPayload
6662
6653
  };
6663
- await this.sendMessageDirect(
6664
- initMessage,
6665
- DEFAULT_MESSAGE_TIMEOUT_MS
6666
- );
6654
+ await this.sendMessageDirect(initMessage, DEFAULT_MESSAGE_TIMEOUT_MS);
6667
6655
  })().finally(() => {
6668
6656
  this.reinitPromise = null;
6669
6657
  });
@@ -6672,10 +6660,7 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6672
6660
  initEventStream() {
6673
6661
  if (this.eventListenerInitialized) return;
6674
6662
  this.eventListenerInitialized = true;
6675
- navigator.serviceWorker.addEventListener(
6676
- "message",
6677
- this.handleEventMessage
6678
- );
6663
+ navigator.serviceWorker.addEventListener("message", this.handleEventMessage);
6679
6664
  }
6680
6665
  handleEventMessage = (event) => {
6681
6666
  const data = event.data;
@@ -6722,9 +6707,6 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
6722
6707
  }
6723
6708
  };
6724
6709
  };
6725
- function getRandomId() {
6726
- return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
6727
- }
6728
6710
 
6729
6711
  // src/repositories/migrationFromContracts.ts
6730
6712
  var MIGRATION_KEY = "migration-from-storage-adapter-swaps";
@@ -6734,8 +6716,14 @@ async function migrateToSwapRepository(storageAdapter, fresh) {
6734
6716
  if (migration === "done") {
6735
6717
  return false;
6736
6718
  }
6737
- const reverseSwaps = await getContractCollection(storageAdapter, "reverseSwaps");
6738
- const submarineSwaps = await getContractCollection(storageAdapter, "submarineSwaps");
6719
+ const reverseSwaps = await getContractCollection(
6720
+ storageAdapter,
6721
+ "reverseSwaps"
6722
+ );
6723
+ const submarineSwaps = await getContractCollection(
6724
+ storageAdapter,
6725
+ "submarineSwaps"
6726
+ );
6739
6727
  for (const swap of reverseSwaps) {
6740
6728
  await fresh.saveSwap(swap);
6741
6729
  }
@@ -6745,9 +6733,7 @@ async function migrateToSwapRepository(storageAdapter, fresh) {
6745
6733
  await storageAdapter.setItem(MIGRATION_KEY, "done");
6746
6734
  return true;
6747
6735
  } catch (error) {
6748
- if (error instanceof Error && error.message.includes(
6749
- "One of the specified object stores was not found."
6750
- )) {
6736
+ if (error instanceof Error && error.message.includes("One of the specified object stores was not found.")) {
6751
6737
  return false;
6752
6738
  }
6753
6739
  throw error;
@@ -6765,6 +6751,30 @@ async function getContractCollection(storage, contractType) {
6765
6751
  );
6766
6752
  }
6767
6753
  }
6754
+
6755
+ // src/repositories/inMemory/swap-repository.ts
6756
+ var InMemorySwapRepository = class {
6757
+ version = 1;
6758
+ swaps = /* @__PURE__ */ new Map();
6759
+ async saveSwap(swap) {
6760
+ this.swaps.set(swap.id, swap);
6761
+ }
6762
+ async deleteSwap(id) {
6763
+ this.swaps.delete(id);
6764
+ }
6765
+ async getAllSwaps(filter) {
6766
+ const swaps = [...this.swaps.values()];
6767
+ if (!filter || Object.keys(filter).length === 0) return swaps;
6768
+ const filtered = applySwapsFilter(swaps, filter);
6769
+ return applyCreatedAtOrder(filtered, filter);
6770
+ }
6771
+ async clear() {
6772
+ this.swaps.clear();
6773
+ }
6774
+ async [Symbol.asyncDispose]() {
6775
+ await this.clear();
6776
+ }
6777
+ };
6768
6778
  // Annotate the CommonJS export names for ESM import in node:
6769
6779
  0 && (module.exports = {
6770
6780
  ArkadeLightningMessageHandler,
@@ -6772,12 +6782,14 @@ async function getContractCollection(storage, contractType) {
6772
6782
  ArkadeSwapsMessageHandler,
6773
6783
  BoltzRefundError,
6774
6784
  BoltzSwapProvider,
6785
+ InMemorySwapRepository,
6775
6786
  IndexedDbSwapRepository,
6776
6787
  InsufficientFundsError,
6777
6788
  InvoiceExpiredError,
6778
6789
  InvoiceFailedToPayError,
6779
6790
  NetworkError,
6780
6791
  PreimageFetchError,
6792
+ QuoteRejectedError,
6781
6793
  SchemaError,
6782
6794
  ServiceWorkerArkadeLightning,
6783
6795
  ServiceWorkerArkadeSwaps,