@arkade-os/boltz-swap 0.3.40 → 0.3.41

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.
package/README.md CHANGED
@@ -61,6 +61,35 @@ console.log('Paid:', result.txid);
61
61
  // SwapManager auto-refunds if payment fails
62
62
  ```
63
63
 
64
+ By default the call resolves only once the swap fully settles (`transaction.claimed`),
65
+ i.e. after Boltz has swept the HTLC. To show an optimistic "sent" state as soon as the
66
+ payment is in flight — like most Lightning wallets — pass `waitFor: 'funded'`:
67
+
68
+ ```typescript
69
+ const result = await swaps.sendLightningPayment({
70
+ invoice: 'lnbc500u1pj...',
71
+ waitFor: 'funded',
72
+ });
73
+ // Resolves as soon as the lockup transaction is observed: the funds are
74
+ // committed and the swap is refundable from here. result.preimage is not
75
+ // reported on this path — the proof of payment is persisted to the stored
76
+ // swap once it settles. Monitoring continues in the
77
+ // background and keeps the stored swap up to date until a terminal status,
78
+ // but a late failure no longer rejects — keep the SwapManager enabled so
79
+ // auto-refunds are handled for you. With the SwapManager disabled, a late
80
+ // failure is only persisted as refundable and the funds stay locked until
81
+ // you recover them via restoreSwaps()/recoverSubmarineFunds().
82
+ ```
83
+
84
+ The same milestone is exposed for a swap you already hold as
85
+ `waitForSwapFunded(pendingSwap)`, alongside `waitForSwapSettlement(pendingSwap)`.
86
+
87
+ If the app restarts before the swap settles, the in-process monitoring is gone — call
88
+ `refreshSwapsStatus()` on startup to reconcile: it polls every pending swap and persists
89
+ the latest status, including the preimage for swaps that settled while the app was
90
+ closed. Read the swap back from the repository (e.g. `getSwapHistory()`) to flip a
91
+ pending UI row to "completed".
92
+
64
93
  ### ARK to BTC
65
94
 
66
95
  ```typescript
@@ -1,5 +1,5 @@
1
1
  import { IWallet, ArkProvider, IndexerProvider, ArkInfo, Identity, ArkTxInput, VHTLC } from '@arkade-os/sdk';
2
- import { r as BoltzSwapProvider, x as SwapManager, n as SwapRepository, q as ArkadeSwapsCreateConfig, A as ArkadeSwapsConfig, o as SwapManagerClient, C as CreateLightningInvoiceRequest, e as CreateLightningInvoiceResponse, b as BoltzReverseSwap, S as SendLightningPaymentRequest, f as SendLightningPaymentResponse, c as BoltzSubmarineSwap, g as SubmarineRefundOutcome, h as SubmarineRecoveryInfo, i as SubmarineRecoveryResult, k as ArkToBtcResponse, a as BoltzChainSwap, m as ChainArkRefundOutcome, l as BtcToArkResponse, d as Chain, F as FeesResponse, j as ChainFeesResponse, L as LimitsResponse, G as GetSwapStatusResponse, B as BoltzSwap } from './types-D97i1LFu.js';
2
+ import { r as BoltzSwapProvider, y as SwapManager, m as SwapRepository, q as ArkadeSwapsCreateConfig, A as ArkadeSwapsConfig, n as SwapManagerClient, C as CreateLightningInvoiceRequest, e as CreateLightningInvoiceResponse, b as BoltzReverseSwap, S as SendLightningPaymentRequest, o as SendLightningPaymentResponse, O as OptimisticSendLightningPaymentResponse, c as BoltzSubmarineSwap, f as SubmarineRefundOutcome, g as SubmarineRecoveryInfo, h as SubmarineRecoveryResult, j as ArkToBtcResponse, a as BoltzChainSwap, l as ChainArkRefundOutcome, k as BtcToArkResponse, d as Chain, F as FeesResponse, i as ChainFeesResponse, L as LimitsResponse, G as GetSwapStatusResponse, B as BoltzSwap } from './types-8NrCdOpS.cjs';
3
3
  import { TransactionOutput } from '@scure/btc-signer/psbt.js';
4
4
 
5
5
  /**
@@ -134,12 +134,33 @@ declare class ArkadeSwaps {
134
134
  }>;
135
135
  /**
136
136
  * Sends a Lightning payment via a submarine swap (Arkade → Lightning).
137
- * Creates the swap, sends funds, and waits for settlement. Auto-refunds on failure.
137
+ * Creates the swap, sends funds, and waits for settlement (or, with
138
+ * `waitFor: "funded"`, only until the lockup transaction is observed —
139
+ * see {@link SendLightningPaymentRequest.waitFor}). Auto-refunds on
140
+ * failures observed before the promise resolves.
138
141
  * @param args.invoice - BOLT11 Lightning invoice to pay.
139
- * @returns The amount paid, preimage (proof of payment), and transaction ID.
142
+ * @param args.waitFor - "settled" (default) resolves with the preimage at
143
+ * "transaction.claimed"; "funded" resolves without a preimage as soon as
144
+ * the payment is in flight.
145
+ * @returns The amount paid, preimage (proof of payment, unless resolved
146
+ * at "funded"), and transaction ID.
140
147
  * @throws {TransactionFailedError} If the payment fails (auto-refunds if possible).
148
+ * @remarks With `waitFor: "funded"`, failures observed *after* the promise
149
+ * resolves are only persisted to the repository (refundable flag) — the
150
+ * active auto-refund in this method is no longer reachable. Keep the
151
+ * SwapManager enabled so late failures are refunded automatically;
152
+ * without it the caller must recover via {@link restoreSwaps} /
153
+ * {@link recoverSubmarineFunds}.
154
+ *
155
+ * Note on types: the overloads narrow on the `waitFor` literal, so a
156
+ * request stored in a variable typed as `SendLightningPaymentRequest`
157
+ * widens the result to {@link OptimisticSendLightningPaymentResponse}
158
+ * (optional preimage) even on the default settled path.
141
159
  */
142
- sendLightningPayment(args: SendLightningPaymentRequest): Promise<SendLightningPaymentResponse>;
160
+ sendLightningPayment(args: SendLightningPaymentRequest & {
161
+ waitFor?: "settled";
162
+ }): Promise<SendLightningPaymentResponse>;
163
+ sendLightningPayment(args: SendLightningPaymentRequest): Promise<OptimisticSendLightningPaymentResponse>;
143
164
  /**
144
165
  * Creates a submarine swap (Arkade → Lightning) and saves it to storage.
145
166
  * @param args.invoice - BOLT11 Lightning invoice to pay.
@@ -229,6 +250,9 @@ declare class ArkadeSwaps {
229
250
  recoverAllSubmarineFunds(swaps: BoltzSubmarineSwap[]): Promise<SubmarineRecoveryResult[]>;
230
251
  /**
231
252
  * Waits for a submarine swap's Lightning payment to settle.
253
+ * Resolves only at the terminal "transaction.claimed" status, once Boltz
254
+ * has swept the HTLC. To resolve as soon as the payment is in flight, use
255
+ * {@link waitForSwapFunded} instead.
232
256
  * @param pendingSwap - The submarine swap to monitor.
233
257
  * @returns The preimage from the settled Lightning payment (proof of payment).
234
258
  * @throws {SwapExpiredError} If the swap expires.
@@ -238,6 +262,32 @@ declare class ArkadeSwaps {
238
262
  waitForSwapSettlement(pendingSwap: BoltzSubmarineSwap): Promise<{
239
263
  preimage: string;
240
264
  }>;
265
+ /**
266
+ * Waits until a submarine swap is funded: resolves as soon as the lockup
267
+ * transaction is observed ("transaction.mempool" or any later status in
268
+ * the lifecycle — statuses can be skipped since subscriptions report only
269
+ * the current one). The sender's funds are committed and the swap is
270
+ * refundable from this point, which is when most Lightning wallets show
271
+ * a payment as "sent".
272
+ *
273
+ * Monitoring continues in the background until the swap reaches a
274
+ * terminal status, persisting updates to the repository (the preimage on
275
+ * claim, the refundable flag on failure), but this promise no longer
276
+ * rejects once resolved — acting on a late failure is the caller's
277
+ * responsibility (the SwapManager handles it automatically when enabled).
278
+ * @param pendingSwap - The submarine swap to monitor.
279
+ * @throws {SwapExpiredError} If the swap expires before funding.
280
+ * @throws {InvoiceFailedToPayError} If Boltz fails to route the payment.
281
+ * @throws {TransactionLockupFailedError} If the lockup transaction fails.
282
+ */
283
+ waitForSwapFunded(pendingSwap: BoltzSubmarineSwap): Promise<void>;
284
+ /**
285
+ * Shared wait machinery: monitors the swap and resolves at the terminal
286
+ * "transaction.claimed" status (with the preimage) or, when `resolveAt`
287
+ * is given, as soon as that status — or any later one in the successful
288
+ * progression — is observed (without a preimage).
289
+ */
290
+ private waitForSubmarineSwap;
241
291
  /**
242
292
  * Creates a chain swap from ARK to BTC.
243
293
  * @param args.btcAddress - Destination Bitcoin address.
@@ -528,7 +578,10 @@ interface IArkadeSwaps extends AsyncDisposable {
528
578
  stopSwapManager(): Promise<void>;
529
579
  getSwapManager(): SwapManagerClient | null;
530
580
  createLightningInvoice(args: CreateLightningInvoiceRequest): Promise<CreateLightningInvoiceResponse>;
531
- sendLightningPayment(args: SendLightningPaymentRequest): Promise<SendLightningPaymentResponse>;
581
+ sendLightningPayment(args: SendLightningPaymentRequest & {
582
+ waitFor?: "settled";
583
+ }): Promise<SendLightningPaymentResponse>;
584
+ sendLightningPayment(args: SendLightningPaymentRequest): Promise<OptimisticSendLightningPaymentResponse>;
532
585
  createSubmarineSwap(args: SendLightningPaymentRequest): Promise<BoltzSubmarineSwap>;
533
586
  createReverseSwap(args: CreateLightningInvoiceRequest): Promise<BoltzReverseSwap>;
534
587
  claimVHTLC(pendingSwap: BoltzReverseSwap): Promise<void>;
@@ -543,6 +596,7 @@ interface IArkadeSwaps extends AsyncDisposable {
543
596
  waitForSwapSettlement(pendingSwap: BoltzSubmarineSwap): Promise<{
544
597
  preimage: string;
545
598
  }>;
599
+ waitForSwapFunded(pendingSwap: BoltzSubmarineSwap): Promise<void>;
546
600
  restoreSwaps(boltzFees?: FeesResponse): Promise<{
547
601
  chainSwaps: BoltzChainSwap[];
548
602
  reverseSwaps: BoltzReverseSwap[];
@@ -1,5 +1,5 @@
1
1
  import { IWallet, ArkProvider, IndexerProvider, ArkInfo, Identity, ArkTxInput, VHTLC } from '@arkade-os/sdk';
2
- import { r as BoltzSwapProvider, x as SwapManager, n as SwapRepository, q as ArkadeSwapsCreateConfig, A as ArkadeSwapsConfig, o as SwapManagerClient, C as CreateLightningInvoiceRequest, e as CreateLightningInvoiceResponse, b as BoltzReverseSwap, S as SendLightningPaymentRequest, f as SendLightningPaymentResponse, c as BoltzSubmarineSwap, g as SubmarineRefundOutcome, h as SubmarineRecoveryInfo, i as SubmarineRecoveryResult, k as ArkToBtcResponse, a as BoltzChainSwap, m as ChainArkRefundOutcome, l as BtcToArkResponse, d as Chain, F as FeesResponse, j as ChainFeesResponse, L as LimitsResponse, G as GetSwapStatusResponse, B as BoltzSwap } from './types-D97i1LFu.cjs';
2
+ import { r as BoltzSwapProvider, y as SwapManager, m as SwapRepository, q as ArkadeSwapsCreateConfig, A as ArkadeSwapsConfig, n as SwapManagerClient, C as CreateLightningInvoiceRequest, e as CreateLightningInvoiceResponse, b as BoltzReverseSwap, S as SendLightningPaymentRequest, o as SendLightningPaymentResponse, O as OptimisticSendLightningPaymentResponse, c as BoltzSubmarineSwap, f as SubmarineRefundOutcome, g as SubmarineRecoveryInfo, h as SubmarineRecoveryResult, j as ArkToBtcResponse, a as BoltzChainSwap, l as ChainArkRefundOutcome, k as BtcToArkResponse, d as Chain, F as FeesResponse, i as ChainFeesResponse, L as LimitsResponse, G as GetSwapStatusResponse, B as BoltzSwap } from './types-8NrCdOpS.js';
3
3
  import { TransactionOutput } from '@scure/btc-signer/psbt.js';
4
4
 
5
5
  /**
@@ -134,12 +134,33 @@ declare class ArkadeSwaps {
134
134
  }>;
135
135
  /**
136
136
  * Sends a Lightning payment via a submarine swap (Arkade → Lightning).
137
- * Creates the swap, sends funds, and waits for settlement. Auto-refunds on failure.
137
+ * Creates the swap, sends funds, and waits for settlement (or, with
138
+ * `waitFor: "funded"`, only until the lockup transaction is observed —
139
+ * see {@link SendLightningPaymentRequest.waitFor}). Auto-refunds on
140
+ * failures observed before the promise resolves.
138
141
  * @param args.invoice - BOLT11 Lightning invoice to pay.
139
- * @returns The amount paid, preimage (proof of payment), and transaction ID.
142
+ * @param args.waitFor - "settled" (default) resolves with the preimage at
143
+ * "transaction.claimed"; "funded" resolves without a preimage as soon as
144
+ * the payment is in flight.
145
+ * @returns The amount paid, preimage (proof of payment, unless resolved
146
+ * at "funded"), and transaction ID.
140
147
  * @throws {TransactionFailedError} If the payment fails (auto-refunds if possible).
148
+ * @remarks With `waitFor: "funded"`, failures observed *after* the promise
149
+ * resolves are only persisted to the repository (refundable flag) — the
150
+ * active auto-refund in this method is no longer reachable. Keep the
151
+ * SwapManager enabled so late failures are refunded automatically;
152
+ * without it the caller must recover via {@link restoreSwaps} /
153
+ * {@link recoverSubmarineFunds}.
154
+ *
155
+ * Note on types: the overloads narrow on the `waitFor` literal, so a
156
+ * request stored in a variable typed as `SendLightningPaymentRequest`
157
+ * widens the result to {@link OptimisticSendLightningPaymentResponse}
158
+ * (optional preimage) even on the default settled path.
141
159
  */
142
- sendLightningPayment(args: SendLightningPaymentRequest): Promise<SendLightningPaymentResponse>;
160
+ sendLightningPayment(args: SendLightningPaymentRequest & {
161
+ waitFor?: "settled";
162
+ }): Promise<SendLightningPaymentResponse>;
163
+ sendLightningPayment(args: SendLightningPaymentRequest): Promise<OptimisticSendLightningPaymentResponse>;
143
164
  /**
144
165
  * Creates a submarine swap (Arkade → Lightning) and saves it to storage.
145
166
  * @param args.invoice - BOLT11 Lightning invoice to pay.
@@ -229,6 +250,9 @@ declare class ArkadeSwaps {
229
250
  recoverAllSubmarineFunds(swaps: BoltzSubmarineSwap[]): Promise<SubmarineRecoveryResult[]>;
230
251
  /**
231
252
  * Waits for a submarine swap's Lightning payment to settle.
253
+ * Resolves only at the terminal "transaction.claimed" status, once Boltz
254
+ * has swept the HTLC. To resolve as soon as the payment is in flight, use
255
+ * {@link waitForSwapFunded} instead.
232
256
  * @param pendingSwap - The submarine swap to monitor.
233
257
  * @returns The preimage from the settled Lightning payment (proof of payment).
234
258
  * @throws {SwapExpiredError} If the swap expires.
@@ -238,6 +262,32 @@ declare class ArkadeSwaps {
238
262
  waitForSwapSettlement(pendingSwap: BoltzSubmarineSwap): Promise<{
239
263
  preimage: string;
240
264
  }>;
265
+ /**
266
+ * Waits until a submarine swap is funded: resolves as soon as the lockup
267
+ * transaction is observed ("transaction.mempool" or any later status in
268
+ * the lifecycle — statuses can be skipped since subscriptions report only
269
+ * the current one). The sender's funds are committed and the swap is
270
+ * refundable from this point, which is when most Lightning wallets show
271
+ * a payment as "sent".
272
+ *
273
+ * Monitoring continues in the background until the swap reaches a
274
+ * terminal status, persisting updates to the repository (the preimage on
275
+ * claim, the refundable flag on failure), but this promise no longer
276
+ * rejects once resolved — acting on a late failure is the caller's
277
+ * responsibility (the SwapManager handles it automatically when enabled).
278
+ * @param pendingSwap - The submarine swap to monitor.
279
+ * @throws {SwapExpiredError} If the swap expires before funding.
280
+ * @throws {InvoiceFailedToPayError} If Boltz fails to route the payment.
281
+ * @throws {TransactionLockupFailedError} If the lockup transaction fails.
282
+ */
283
+ waitForSwapFunded(pendingSwap: BoltzSubmarineSwap): Promise<void>;
284
+ /**
285
+ * Shared wait machinery: monitors the swap and resolves at the terminal
286
+ * "transaction.claimed" status (with the preimage) or, when `resolveAt`
287
+ * is given, as soon as that status — or any later one in the successful
288
+ * progression — is observed (without a preimage).
289
+ */
290
+ private waitForSubmarineSwap;
241
291
  /**
242
292
  * Creates a chain swap from ARK to BTC.
243
293
  * @param args.btcAddress - Destination Bitcoin address.
@@ -528,7 +578,10 @@ interface IArkadeSwaps extends AsyncDisposable {
528
578
  stopSwapManager(): Promise<void>;
529
579
  getSwapManager(): SwapManagerClient | null;
530
580
  createLightningInvoice(args: CreateLightningInvoiceRequest): Promise<CreateLightningInvoiceResponse>;
531
- sendLightningPayment(args: SendLightningPaymentRequest): Promise<SendLightningPaymentResponse>;
581
+ sendLightningPayment(args: SendLightningPaymentRequest & {
582
+ waitFor?: "settled";
583
+ }): Promise<SendLightningPaymentResponse>;
584
+ sendLightningPayment(args: SendLightningPaymentRequest): Promise<OptimisticSendLightningPaymentResponse>;
532
585
  createSubmarineSwap(args: SendLightningPaymentRequest): Promise<BoltzSubmarineSwap>;
533
586
  createReverseSwap(args: CreateLightningInvoiceRequest): Promise<BoltzReverseSwap>;
534
587
  claimVHTLC(pendingSwap: BoltzReverseSwap): Promise<void>;
@@ -543,6 +596,7 @@ interface IArkadeSwaps extends AsyncDisposable {
543
596
  waitForSwapSettlement(pendingSwap: BoltzSubmarineSwap): Promise<{
544
597
  preimage: string;
545
598
  }>;
599
+ waitForSwapFunded(pendingSwap: BoltzSubmarineSwap): Promise<void>;
546
600
  restoreSwaps(boltzFees?: FeesResponse): Promise<{
547
601
  chainSwaps: BoltzChainSwap[];
548
602
  reverseSwaps: BoltzReverseSwap[];
@@ -7,7 +7,7 @@ import {
7
7
  isSubmarineFinalStatus,
8
8
  isSubmarineSwapRefundable,
9
9
  logger
10
- } from "./chunk-GYWMQOCP.js";
10
+ } from "./chunk-UXYHW7KV.js";
11
11
 
12
12
  // src/expo/swapsPollProcessor.ts
13
13
  var SWAP_POLL_TASK_TYPE = "swap-poll";
@@ -233,6 +233,22 @@ var isSubmarinePendingStatus = (status) => {
233
233
  var isSubmarineRefundableStatus = (status) => {
234
234
  return ["invoice.failedToPay", "transaction.lockupFailed", "swap.expired"].includes(status);
235
235
  };
236
+ var SUBMARINE_STATUS_PROGRESSION = [
237
+ "swap.created",
238
+ "invoice.set",
239
+ "transaction.mempool",
240
+ "transaction.confirmed",
241
+ "invoice.pending",
242
+ "invoice.paid",
243
+ "transaction.claim.pending",
244
+ "transaction.claimed"
245
+ ];
246
+ var hasSubmarineStatusReached = (status, target) => {
247
+ const progression = SUBMARINE_STATUS_PROGRESSION;
248
+ const statusIndex = progression.indexOf(status);
249
+ const targetIndex = progression.indexOf(target);
250
+ return statusIndex >= 0 && targetIndex >= 0 && statusIndex >= targetIndex;
251
+ };
236
252
  var isSubmarineSuccessStatus = (status) => {
237
253
  return status === "transaction.claimed";
238
254
  };
@@ -3599,16 +3615,6 @@ var ArkadeSwaps = class _ArkadeSwaps {
3599
3615
  this.swapProvider.monitorSwap(pendingSwap.id, onStatusUpdate).catch(reject);
3600
3616
  });
3601
3617
  }
3602
- // =========================================================================
3603
- // Lightning: Submarine swaps (send Arkade -> Lightning)
3604
- // =========================================================================
3605
- /**
3606
- * Sends a Lightning payment via a submarine swap (Arkade → Lightning).
3607
- * Creates the swap, sends funds, and waits for settlement. Auto-refunds on failure.
3608
- * @param args.invoice - BOLT11 Lightning invoice to pay.
3609
- * @returns The amount paid, preimage (proof of payment), and transaction ID.
3610
- * @throws {TransactionFailedError} If the payment fails (auto-refunds if possible).
3611
- */
3612
3618
  async sendLightningPayment(args) {
3613
3619
  const pendingSwap = await this.createSubmarineSwap(args);
3614
3620
  if (!pendingSwap.response.address)
@@ -3619,6 +3625,18 @@ var ArkadeSwaps = class _ArkadeSwaps {
3619
3625
  amount: pendingSwap.response.expectedAmount
3620
3626
  });
3621
3627
  try {
3628
+ if (args.waitFor === "funded") {
3629
+ if (!this.swapManager) {
3630
+ logger.warn(
3631
+ `Swap ${pendingSwap.id}: sendLightningPayment with waitFor "funded" but SwapManager is disabled \u2014 a failure after this promise resolves is only persisted as refundable, not auto-refunded; recover via restoreSwaps/recoverSubmarineFunds`
3632
+ );
3633
+ }
3634
+ await this.waitForSwapFunded(pendingSwap);
3635
+ return {
3636
+ amount: pendingSwap.response.expectedAmount,
3637
+ txid
3638
+ };
3639
+ }
3622
3640
  const { preimage } = await this.waitForSwapSettlement(pendingSwap);
3623
3641
  return {
3624
3642
  amount: pendingSwap.response.expectedAmount,
@@ -4120,6 +4138,9 @@ var ArkadeSwaps = class _ArkadeSwaps {
4120
4138
  }
4121
4139
  /**
4122
4140
  * Waits for a submarine swap's Lightning payment to settle.
4141
+ * Resolves only at the terminal "transaction.claimed" status, once Boltz
4142
+ * has swept the HTLC. To resolve as soon as the payment is in flight, use
4143
+ * {@link waitForSwapFunded} instead.
4123
4144
  * @param pendingSwap - The submarine swap to monitor.
4124
4145
  * @returns The preimage from the settled Lightning payment (proof of payment).
4125
4146
  * @throws {SwapExpiredError} If the swap expires.
@@ -4127,19 +4148,59 @@ var ArkadeSwaps = class _ArkadeSwaps {
4127
4148
  * @throws {TransactionLockupFailedError} If the lockup transaction fails.
4128
4149
  */
4129
4150
  async waitForSwapSettlement(pendingSwap) {
4151
+ return this.waitForSubmarineSwap(pendingSwap);
4152
+ }
4153
+ /**
4154
+ * Waits until a submarine swap is funded: resolves as soon as the lockup
4155
+ * transaction is observed ("transaction.mempool" or any later status in
4156
+ * the lifecycle — statuses can be skipped since subscriptions report only
4157
+ * the current one). The sender's funds are committed and the swap is
4158
+ * refundable from this point, which is when most Lightning wallets show
4159
+ * a payment as "sent".
4160
+ *
4161
+ * Monitoring continues in the background until the swap reaches a
4162
+ * terminal status, persisting updates to the repository (the preimage on
4163
+ * claim, the refundable flag on failure), but this promise no longer
4164
+ * rejects once resolved — acting on a late failure is the caller's
4165
+ * responsibility (the SwapManager handles it automatically when enabled).
4166
+ * @param pendingSwap - The submarine swap to monitor.
4167
+ * @throws {SwapExpiredError} If the swap expires before funding.
4168
+ * @throws {InvoiceFailedToPayError} If Boltz fails to route the payment.
4169
+ * @throws {TransactionLockupFailedError} If the lockup transaction fails.
4170
+ */
4171
+ async waitForSwapFunded(pendingSwap) {
4172
+ await this.waitForSubmarineSwap(pendingSwap, "transaction.mempool");
4173
+ }
4174
+ /**
4175
+ * Shared wait machinery: monitors the swap and resolves at the terminal
4176
+ * "transaction.claimed" status (with the preimage) or, when `resolveAt`
4177
+ * is given, as soon as that status — or any later one in the successful
4178
+ * progression — is observed (without a preimage).
4179
+ */
4180
+ async waitForSubmarineSwap(pendingSwap, resolveAt) {
4130
4181
  return new Promise((resolve, reject) => {
4131
- let isResolved = false;
4182
+ let isFinal = false;
4183
+ let isSettled = false;
4132
4184
  const onStatusUpdate = async (status) => {
4133
- if (isResolved) return;
4134
- const saveStatus = (additionalFields) => updateSubmarineSwapStatus(
4135
- pendingSwap,
4136
- status,
4137
- this.savePendingSubmarineSwap.bind(this),
4138
- additionalFields
4139
- );
4185
+ if (isFinal) return;
4186
+ const saveStatus = async (additionalFields) => {
4187
+ try {
4188
+ await updateSubmarineSwapStatus(
4189
+ pendingSwap,
4190
+ status,
4191
+ this.savePendingSubmarineSwap.bind(this),
4192
+ additionalFields
4193
+ );
4194
+ } catch (error) {
4195
+ logger.error(
4196
+ `Swap ${pendingSwap.id}: failed to persist status "${status}": ${error}`
4197
+ );
4198
+ }
4199
+ };
4140
4200
  switch (status) {
4141
4201
  case "swap.expired":
4142
- isResolved = true;
4202
+ isFinal = true;
4203
+ isSettled = true;
4143
4204
  await saveStatus({ refundable: true });
4144
4205
  reject(
4145
4206
  new SwapExpiredError({
@@ -4149,7 +4210,8 @@ var ArkadeSwaps = class _ArkadeSwaps {
4149
4210
  );
4150
4211
  break;
4151
4212
  case "invoice.failedToPay":
4152
- isResolved = true;
4213
+ isFinal = true;
4214
+ isSettled = true;
4153
4215
  await saveStatus({ refundable: true });
4154
4216
  reject(
4155
4217
  new InvoiceFailedToPayError({
@@ -4159,7 +4221,8 @@ var ArkadeSwaps = class _ArkadeSwaps {
4159
4221
  );
4160
4222
  break;
4161
4223
  case "transaction.lockupFailed":
4162
- isResolved = true;
4224
+ isFinal = true;
4225
+ isSettled = true;
4163
4226
  await saveStatus({ refundable: true });
4164
4227
  reject(
4165
4228
  new TransactionLockupFailedError({
@@ -4169,23 +4232,47 @@ var ArkadeSwaps = class _ArkadeSwaps {
4169
4232
  );
4170
4233
  break;
4171
4234
  case "transaction.claimed": {
4172
- isResolved = true;
4173
- const { preimage } = await this.swapProvider.getSwapPreimage(
4174
- pendingSwap.id
4175
- );
4176
- await saveStatus({ preimage });
4177
- resolve({ preimage });
4235
+ isFinal = true;
4236
+ isSettled = true;
4237
+ try {
4238
+ const { preimage } = await this.swapProvider.getSwapPreimage(
4239
+ pendingSwap.id
4240
+ );
4241
+ await saveStatus({ preimage });
4242
+ resolve({ preimage });
4243
+ } catch (error) {
4244
+ logger.error(
4245
+ `Swap ${pendingSwap.id}: failed to fetch preimage on claim: ${error}`
4246
+ );
4247
+ reject(error);
4248
+ }
4178
4249
  break;
4179
4250
  }
4180
4251
  default:
4181
4252
  await saveStatus();
4253
+ if (resolveAt && hasSubmarineStatusReached(status, resolveAt)) {
4254
+ isSettled = true;
4255
+ resolve({ preimage: void 0 });
4256
+ }
4182
4257
  break;
4183
4258
  }
4184
4259
  };
4185
- this.swapProvider.monitorSwap(pendingSwap.id, onStatusUpdate).catch((error) => {
4186
- if (!isResolved) {
4187
- isResolved = true;
4260
+ this.swapProvider.monitorSwap(pendingSwap.id, (status) => {
4261
+ onStatusUpdate(status).catch(
4262
+ (error) => logger.error(
4263
+ `Swap ${pendingSwap.id}: error handling status "${status}": ${error}`
4264
+ )
4265
+ );
4266
+ }).catch((error) => {
4267
+ if (!isSettled) {
4268
+ isFinal = true;
4269
+ isSettled = true;
4188
4270
  reject(error);
4271
+ } else {
4272
+ isFinal = true;
4273
+ logger.warn(
4274
+ `Swap ${pendingSwap.id}: monitor failed after settlement: ${error}`
4275
+ );
4189
4276
  }
4190
4277
  });
4191
4278
  });
@@ -5209,13 +5296,28 @@ var ArkadeSwaps = class _ArkadeSwaps {
5209
5296
  for (const swap of await this.getPendingSubmarineSwapsFromStorage()) {
5210
5297
  if (isSubmarineFinalStatus(swap.status)) continue;
5211
5298
  promises.push(
5212
- this.getSwapStatus(swap.id).then(
5213
- ({ status }) => updateSubmarineSwapStatus(
5299
+ this.getSwapStatus(swap.id).then(async ({ status }) => {
5300
+ let additionalFields;
5301
+ if (isSubmarineSuccessStatus(status) && !swap.preimage) {
5302
+ try {
5303
+ const { preimage } = await this.swapProvider.getSwapPreimage(
5304
+ swap.id
5305
+ );
5306
+ additionalFields = { preimage };
5307
+ } catch (error) {
5308
+ logger.warn(
5309
+ `Failed to fetch preimage for settled swap ${swap.id}:`,
5310
+ error
5311
+ );
5312
+ }
5313
+ }
5314
+ await updateSubmarineSwapStatus(
5214
5315
  swap,
5215
5316
  status,
5216
- this.savePendingSubmarineSwap.bind(this)
5217
- )
5218
- ).catch((error) => {
5317
+ this.savePendingSubmarineSwap.bind(this),
5318
+ additionalFields
5319
+ );
5320
+ }).catch((error) => {
5219
5321
  logger.error(`Failed to refresh swap status for ${swap.id}:`, error);
5220
5322
  })
5221
5323
  );
@@ -5385,6 +5487,7 @@ export {
5385
5487
  isSubmarineFinalStatus,
5386
5488
  isSubmarinePendingStatus,
5387
5489
  isSubmarineRefundableStatus,
5490
+ hasSubmarineStatusReached,
5388
5491
  isSubmarineSuccessStatus,
5389
5492
  isReverseFailedStatus,
5390
5493
  isReverseFinalStatus,