@arkade-os/boltz-swap 0.3.29 → 0.3.31

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
@@ -260,6 +260,21 @@ Custom implementations must set `readonly version = 1` — TypeScript will error
260
260
 
261
261
  Expo/React Native cannot run a long-lived Service Worker, and background work is executed by the OS for a short window (typically every ~15+ minutes). To enable best-effort background claim/refund for swaps, use `ExpoArkadeLightning` plus a background task defined at global scope.
262
262
 
263
+ > [!WARNING]
264
+ > **Change since 0.3.30** — fix for [#136](https://github.com/arkade-os/boltz-swap/issues/136).
265
+ >
266
+ > Background task helpers moved from `@arkade-os/boltz-swap/expo` to
267
+ > `@arkade-os/boltz-swap/expo/background`. OS-level registration is no
268
+ > longer performed by `ExpoArkadeSwaps.setup()` — call it explicitly.
269
+ >
270
+ > | Before | After |
271
+ > | --- | --- |
272
+ > | `import { defineExpoSwapBackgroundTask } from "@arkade-os/boltz-swap/expo"` | `import { defineExpoSwapBackgroundTask } from "@arkade-os/boltz-swap/expo/background"` |
273
+ > | `background: { taskName, taskQueue, minimumBackgroundInterval, foregroundIntervalMs }` | `background: { taskQueue, foregroundIntervalMs }` + explicit `await registerExpoSwapBackgroundTask(taskName, { minimumInterval })` |
274
+ > | `dispose()` unregistered the OS task | Call `unregisterExpoSwapBackgroundTask(taskName)` yourself |
275
+ >
276
+ > TypeScript callers get a compile error on the removed fields. **JS callers must update manually** — the old fields are silently ignored and the OS task will never run.
277
+
263
278
  ### Prerequisites
264
279
 
265
280
  - Install Expo background task dependencies:
@@ -298,7 +313,7 @@ import * as SecureStore from "expo-secure-store";
298
313
  import { SingleKey } from "@arkade-os/sdk";
299
314
  import { AsyncStorageTaskQueue } from "@arkade-os/sdk/worker/expo";
300
315
  import { IndexedDbSwapRepository } from "@arkade-os/boltz-swap";
301
- import { defineExpoSwapBackgroundTask } from "@arkade-os/boltz-swap/expo";
316
+ import { defineExpoSwapBackgroundTask } from "@arkade-os/boltz-swap/expo/background";
302
317
 
303
318
  const swapTaskQueue = new AsyncStorageTaskQueue(AsyncStorage, "ark:swap-queue");
304
319
  const swapRepository = new IndexedDbSwapRepository();
@@ -324,6 +339,7 @@ import { ExpoWallet } from "@arkade-os/sdk/wallet/expo";
324
339
  import { AsyncStorageTaskQueue } from "@arkade-os/sdk/worker/expo";
325
340
  import { BoltzSwapProvider } from "@arkade-os/boltz-swap";
326
341
  import { ExpoArkadeLightning } from "@arkade-os/boltz-swap/expo";
342
+ import { registerExpoSwapBackgroundTask } from "@arkade-os/boltz-swap/expo/background";
327
343
 
328
344
  // Used by ExpoWallet's background task (defined via @arkade-os/sdk/wallet/expo)
329
345
  const walletTaskQueue = new AsyncStorageTaskQueue(AsyncStorage, "ark:wallet-queue");
@@ -350,16 +366,31 @@ const arkLn = await ExpoArkadeLightning.setup({
350
366
  swapProvider,
351
367
  swapRepository, // must match the one used in defineExpoSwapBackgroundTask
352
368
  background: {
353
- taskName: "ark-swap-poll",
354
369
  taskQueue: swapTaskQueue, // must match the one used in defineExpoSwapBackgroundTask
355
370
  foregroundIntervalMs: 20_000,
356
- minimumBackgroundInterval: 15,
357
371
  },
358
372
  });
359
373
 
374
+ // Activate the OS scheduler (Expo Android/iOS only).
375
+ // Must use the same task name passed to defineExpoSwapBackgroundTask above.
376
+ await registerExpoSwapBackgroundTask("ark-swap-poll", { minimumInterval: 15 });
377
+
360
378
  await arkLn.createLightningInvoice({ amount: 1000 });
379
+
380
+ // On logout / wallet reset / app teardown:
381
+ import { unregisterExpoSwapBackgroundTask } from "@arkade-os/boltz-swap/expo/background";
382
+ await arkLn.dispose();
383
+ await unregisterExpoSwapBackgroundTask("ark-swap-poll");
361
384
  ```
362
385
 
386
+ > [!IMPORTANT]
387
+ > The OS-task helpers (`defineExpoSwapBackgroundTask`,
388
+ > `registerExpoSwapBackgroundTask`, `unregisterExpoSwapBackgroundTask`)
389
+ > live under `@arkade-os/boltz-swap/expo/background`. That subpath is
390
+ > the **only** module that imports `expo-task-manager` /
391
+ > `expo-background-task`; keeping it isolated lets react-native-web and
392
+ > Node consumers use `/expo` without those native packages.
393
+
363
394
  ### Error Handling
364
395
 
365
396
  With SwapManager, refunds are automatic — listen to `onSwapFailed` for notifications. Without it, handle errors manually:
@@ -335,7 +335,7 @@ var BoltzSwapProvider = class {
335
335
  /** @param config Provider configuration with network and optional API URL. */
336
336
  constructor(config) {
337
337
  this.network = config.network;
338
- this.referralId = config.referralId;
338
+ this.referralId = config.referralId ?? "arkade-ts-sdk";
339
339
  const apiUrl = config.apiUrl || BASE_URLS[config.network];
340
340
  if (!apiUrl)
341
341
  throw new Error(
@@ -0,0 +1,117 @@
1
+ import {
2
+ ArkadeSwaps,
3
+ isPendingReverseSwap,
4
+ isPendingSubmarineSwap,
5
+ isReverseClaimableStatus,
6
+ isReverseFinalStatus,
7
+ isSubmarineFinalStatus,
8
+ isSubmarineSwapRefundable,
9
+ logger
10
+ } from "./chunk-B3Q4TFWT.js";
11
+
12
+ // src/expo/swapsPollProcessor.ts
13
+ var SWAP_POLL_TASK_TYPE = "swap-poll";
14
+ var swapsPollProcessor = {
15
+ taskType: SWAP_POLL_TASK_TYPE,
16
+ async execute(item, deps) {
17
+ const {
18
+ swapRepository,
19
+ swapProvider,
20
+ wallet,
21
+ arkProvider,
22
+ indexerProvider
23
+ } = deps;
24
+ const allSwaps = await swapRepository.getAllSwaps();
25
+ const pendingSwaps = allSwaps.filter((swap) => {
26
+ if (isPendingReverseSwap(swap))
27
+ return !isReverseFinalStatus(swap.status);
28
+ if (isPendingSubmarineSwap(swap))
29
+ return !isSubmarineFinalStatus(swap.status);
30
+ return false;
31
+ });
32
+ let polled = 0;
33
+ let updated = 0;
34
+ let claimed = 0;
35
+ let refunded = 0;
36
+ let errors = 0;
37
+ const tempSwaps = new ArkadeSwaps({
38
+ wallet,
39
+ arkProvider,
40
+ indexerProvider,
41
+ swapProvider,
42
+ swapManager: false,
43
+ swapRepository
44
+ });
45
+ try {
46
+ for (const swap of pendingSwaps) {
47
+ try {
48
+ const { status: currentStatus } = await swapProvider.getSwapStatus(swap.id);
49
+ polled++;
50
+ if (currentStatus !== swap.status) {
51
+ await swapRepository.saveSwap({
52
+ ...swap,
53
+ status: currentStatus
54
+ });
55
+ updated++;
56
+ }
57
+ if (isPendingReverseSwap(swap) && isReverseClaimableStatus(currentStatus)) {
58
+ if (!swap.preimage) {
59
+ logger.warn(
60
+ `[swap-poll] Skipping claim for ${swap.id}: no preimage`
61
+ );
62
+ continue;
63
+ }
64
+ try {
65
+ await tempSwaps.claimVHTLC(swap);
66
+ claimed++;
67
+ } catch (claimError) {
68
+ logger.error(
69
+ `[swap-poll] Claim failed for ${swap.id}:`,
70
+ claimError
71
+ );
72
+ errors++;
73
+ }
74
+ }
75
+ const swapWithStatus = isPendingSubmarineSwap(swap) ? { ...swap, status: currentStatus } : null;
76
+ if (isPendingSubmarineSwap(swap) && isSubmarineSwapRefundable(swapWithStatus)) {
77
+ if (!swap.request.invoice && !swap.preimageHash) {
78
+ logger.warn(
79
+ `[swap-poll] Skipping refund for ${swap.id}: no invoice or preimageHash`
80
+ );
81
+ continue;
82
+ }
83
+ try {
84
+ await tempSwaps.refundVHTLC(swapWithStatus);
85
+ refunded++;
86
+ } catch (refundError) {
87
+ logger.error(
88
+ `[swap-poll] Refund failed for ${swap.id}:`,
89
+ refundError
90
+ );
91
+ errors++;
92
+ }
93
+ }
94
+ } catch (swapError) {
95
+ logger.error(
96
+ `[swap-poll] Error processing swap ${swap.id}:`,
97
+ swapError
98
+ );
99
+ errors++;
100
+ }
101
+ }
102
+ } finally {
103
+ await tempSwaps.dispose();
104
+ }
105
+ return {
106
+ taskItemId: item.id,
107
+ type: SWAP_POLL_TASK_TYPE,
108
+ status: errors > 0 && polled === 0 ? "failed" : "success",
109
+ data: { polled, updated, claimed, refunded, errors }
110
+ };
111
+ }
112
+ };
113
+
114
+ export {
115
+ SWAP_POLL_TASK_TYPE,
116
+ swapsPollProcessor
117
+ };