@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.
@@ -0,0 +1,93 @@
1
+ import { AsyncStorageTaskQueue, TaskProcessor } from '@arkade-os/sdk/worker/expo';
2
+ import { ArkProvider, IndexerProvider, Identity, IWallet } from '@arkade-os/sdk';
3
+ import { m as SwapRepository, p as BoltzSwapProvider, N as Network, A as ArkadeSwapsConfig } from './types-BBI7-KJ0.cjs';
4
+
5
+ /**
6
+ * Dependencies injected into every swap processor at runtime.
7
+ *
8
+ * Unlike the wallet's `TaskDependencies`, these are swap-specific:
9
+ * we need the Boltz provider, swap repository, and identity to
10
+ * poll status and attempt claim/refund.
11
+ */
12
+ interface SwapTaskDependencies {
13
+ swapRepository: SwapRepository;
14
+ swapProvider: BoltzSwapProvider;
15
+ arkProvider: ArkProvider;
16
+ indexerProvider: IndexerProvider;
17
+ identity: Identity;
18
+ wallet: IWallet;
19
+ }
20
+ /**
21
+ * Minimal config persisted to AsyncStorage for background rehydration.
22
+ *
23
+ * The background handler runs in a fresh JS context without access to
24
+ * the foreground's in-memory state, so we persist just enough to
25
+ * reconstruct providers and identity.
26
+ */
27
+ interface PersistedSwapBackgroundConfig {
28
+ boltzApiUrl: string;
29
+ arkServerUrl: string;
30
+ network: Network;
31
+ }
32
+ /**
33
+ * Background scheduling configuration for {@link ExpoArkadeSwaps}.
34
+ *
35
+ * OS-level task registration is **not** part of this config — call
36
+ * `registerExpoSwapBackgroundTask` from `@arkade-os/boltz-swap/expo/background`
37
+ * explicitly. Splitting that step out keeps `/expo` free of the
38
+ * `expo-task-manager` / `expo-background-task` dependencies.
39
+ */
40
+ interface ExpoSwapBackgroundConfig {
41
+ /** Persistence layer for foreground ↔ background handoff. */
42
+ taskQueue: AsyncStorageTaskQueue;
43
+ /** If set, acknowledges background results at this interval (ms) while the app is in the foreground. */
44
+ foregroundIntervalMs?: number;
45
+ }
46
+ /**
47
+ * Options for {@link defineExpoSwapBackgroundTask}.
48
+ */
49
+ interface DefineSwapBackgroundTaskOptions {
50
+ /** AsyncStorage-backed queue (must match the one passed to ExpoArkadeSwaps.setup). */
51
+ taskQueue: AsyncStorageTaskQueue;
52
+ /** Swap repository (fresh instance is fine — connects to the same DB). */
53
+ swapRepository: SwapRepository;
54
+ /** Factory to reconstruct Identity from secure storage in the background. */
55
+ identityFactory: () => Promise<Identity>;
56
+ }
57
+ /**
58
+ * Configuration for {@link ExpoArkadeSwaps.setup}.
59
+ */
60
+ interface ExpoArkadeSwapsConfig extends ArkadeSwapsConfig {
61
+ /**
62
+ * Ark server base URL (e.g. "https://ark.example.com").
63
+ *
64
+ * Recommended for type-safe background rehydration. If omitted,
65
+ * ExpoArkadeSwaps will attempt to derive it from the ArkProvider.
66
+ */
67
+ arkServerUrl?: string;
68
+ background: ExpoSwapBackgroundConfig;
69
+ }
70
+ /** @deprecated Use ExpoArkadeSwapsConfig instead */
71
+ type ExpoArkadeLightningConfig = ExpoArkadeSwapsConfig;
72
+
73
+ /**
74
+ * Task type identifier for {@link swapsPollProcessor}.
75
+ */
76
+ declare const SWAP_POLL_TASK_TYPE = "swap-poll";
77
+ /**
78
+ * Stateless processor that polls Boltz for swap status updates and
79
+ * attempts best-effort claim/refund for actionable swaps.
80
+ *
81
+ * Designed for Expo background tasks (~30s window) and follows the
82
+ * same `TaskProcessor` pattern as `contractPollProcessor` in ts-sdk.
83
+ *
84
+ * Steps:
85
+ * 1. Read all non-final swaps from SwapRepository
86
+ * 2. Poll Boltz HTTP API for each swap's current status
87
+ * 3. Persist status changes immediately
88
+ * 4. For actionable statuses: attempt claimVHTLC / refundVHTLC (best-effort)
89
+ * 5. Return summary metrics
90
+ */
91
+ declare const swapsPollProcessor: TaskProcessor<SwapTaskDependencies>;
92
+
93
+ export { type DefineSwapBackgroundTaskOptions as D, type ExpoArkadeSwapsConfig as E, type PersistedSwapBackgroundConfig as P, SWAP_POLL_TASK_TYPE as S, type SwapTaskDependencies as a, type ExpoSwapBackgroundConfig as b, type ExpoArkadeLightningConfig as c, swapsPollProcessor as s };
@@ -0,0 +1,93 @@
1
+ import { AsyncStorageTaskQueue, TaskProcessor } from '@arkade-os/sdk/worker/expo';
2
+ import { ArkProvider, IndexerProvider, Identity, IWallet } from '@arkade-os/sdk';
3
+ import { m as SwapRepository, p as BoltzSwapProvider, N as Network, A as ArkadeSwapsConfig } from './types-BBI7-KJ0.js';
4
+
5
+ /**
6
+ * Dependencies injected into every swap processor at runtime.
7
+ *
8
+ * Unlike the wallet's `TaskDependencies`, these are swap-specific:
9
+ * we need the Boltz provider, swap repository, and identity to
10
+ * poll status and attempt claim/refund.
11
+ */
12
+ interface SwapTaskDependencies {
13
+ swapRepository: SwapRepository;
14
+ swapProvider: BoltzSwapProvider;
15
+ arkProvider: ArkProvider;
16
+ indexerProvider: IndexerProvider;
17
+ identity: Identity;
18
+ wallet: IWallet;
19
+ }
20
+ /**
21
+ * Minimal config persisted to AsyncStorage for background rehydration.
22
+ *
23
+ * The background handler runs in a fresh JS context without access to
24
+ * the foreground's in-memory state, so we persist just enough to
25
+ * reconstruct providers and identity.
26
+ */
27
+ interface PersistedSwapBackgroundConfig {
28
+ boltzApiUrl: string;
29
+ arkServerUrl: string;
30
+ network: Network;
31
+ }
32
+ /**
33
+ * Background scheduling configuration for {@link ExpoArkadeSwaps}.
34
+ *
35
+ * OS-level task registration is **not** part of this config — call
36
+ * `registerExpoSwapBackgroundTask` from `@arkade-os/boltz-swap/expo/background`
37
+ * explicitly. Splitting that step out keeps `/expo` free of the
38
+ * `expo-task-manager` / `expo-background-task` dependencies.
39
+ */
40
+ interface ExpoSwapBackgroundConfig {
41
+ /** Persistence layer for foreground ↔ background handoff. */
42
+ taskQueue: AsyncStorageTaskQueue;
43
+ /** If set, acknowledges background results at this interval (ms) while the app is in the foreground. */
44
+ foregroundIntervalMs?: number;
45
+ }
46
+ /**
47
+ * Options for {@link defineExpoSwapBackgroundTask}.
48
+ */
49
+ interface DefineSwapBackgroundTaskOptions {
50
+ /** AsyncStorage-backed queue (must match the one passed to ExpoArkadeSwaps.setup). */
51
+ taskQueue: AsyncStorageTaskQueue;
52
+ /** Swap repository (fresh instance is fine — connects to the same DB). */
53
+ swapRepository: SwapRepository;
54
+ /** Factory to reconstruct Identity from secure storage in the background. */
55
+ identityFactory: () => Promise<Identity>;
56
+ }
57
+ /**
58
+ * Configuration for {@link ExpoArkadeSwaps.setup}.
59
+ */
60
+ interface ExpoArkadeSwapsConfig extends ArkadeSwapsConfig {
61
+ /**
62
+ * Ark server base URL (e.g. "https://ark.example.com").
63
+ *
64
+ * Recommended for type-safe background rehydration. If omitted,
65
+ * ExpoArkadeSwaps will attempt to derive it from the ArkProvider.
66
+ */
67
+ arkServerUrl?: string;
68
+ background: ExpoSwapBackgroundConfig;
69
+ }
70
+ /** @deprecated Use ExpoArkadeSwapsConfig instead */
71
+ type ExpoArkadeLightningConfig = ExpoArkadeSwapsConfig;
72
+
73
+ /**
74
+ * Task type identifier for {@link swapsPollProcessor}.
75
+ */
76
+ declare const SWAP_POLL_TASK_TYPE = "swap-poll";
77
+ /**
78
+ * Stateless processor that polls Boltz for swap status updates and
79
+ * attempts best-effort claim/refund for actionable swaps.
80
+ *
81
+ * Designed for Expo background tasks (~30s window) and follows the
82
+ * same `TaskProcessor` pattern as `contractPollProcessor` in ts-sdk.
83
+ *
84
+ * Steps:
85
+ * 1. Read all non-final swaps from SwapRepository
86
+ * 2. Poll Boltz HTTP API for each swap's current status
87
+ * 3. Persist status changes immediately
88
+ * 4. For actionable statuses: attempt claimVHTLC / refundVHTLC (best-effort)
89
+ * 5. Return summary metrics
90
+ */
91
+ declare const swapsPollProcessor: TaskProcessor<SwapTaskDependencies>;
92
+
93
+ export { type DefineSwapBackgroundTaskOptions as D, type ExpoArkadeSwapsConfig as E, type PersistedSwapBackgroundConfig as P, SWAP_POLL_TASK_TYPE as S, type SwapTaskDependencies as a, type ExpoSwapBackgroundConfig as b, type ExpoArkadeLightningConfig as c, swapsPollProcessor as s };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arkade-os/boltz-swap",
3
- "version": "0.3.29",
3
+ "version": "0.3.31",
4
4
  "type": "module",
5
5
  "description": "A production-ready TypeScript package that brings Boltz submarine-swaps to Arkade.",
6
6
  "main": "./dist/index.js",
@@ -26,6 +26,16 @@
26
26
  "default": "./dist/expo/index.cjs"
27
27
  }
28
28
  },
29
+ "./expo/background": {
30
+ "import": {
31
+ "types": "./dist/expo/background.d.ts",
32
+ "default": "./dist/expo/background.js"
33
+ },
34
+ "require": {
35
+ "types": "./dist/expo/background.d.ts",
36
+ "default": "./dist/expo/background.cjs"
37
+ }
38
+ },
29
39
  "./repositories/sqlite": {
30
40
  "import": {
31
41
  "types": "./dist/repositories/sqlite/index.d.ts",
@@ -60,7 +70,7 @@
60
70
  "author": "Arkade-OS",
61
71
  "license": "MIT",
62
72
  "dependencies": {
63
- "@arkade-os/sdk": "0.4.25",
73
+ "@arkade-os/sdk": "0.4.26",
64
74
  "@noble/hashes": "2.0.1",
65
75
  "@scure/base": "2.0.0",
66
76
  "@scure/btc-signer": "2.0.1",
@@ -68,6 +78,18 @@
68
78
  "@noble/curves": "^2.0.1",
69
79
  "light-bolt11-decoder": "3.2.0"
70
80
  },
81
+ "peerDependencies": {
82
+ "expo-task-manager": ">=3.0.0",
83
+ "expo-background-task": ">=0.1.0"
84
+ },
85
+ "peerDependenciesMeta": {
86
+ "expo-task-manager": {
87
+ "optional": true
88
+ },
89
+ "expo-background-task": {
90
+ "optional": true
91
+ }
92
+ },
71
93
  "devDependencies": {
72
94
  "@eslint/js": "^9.35.0",
73
95
  "@types/node": "^24.3.1",
@@ -91,7 +113,7 @@
91
113
  "access": "public"
92
114
  },
93
115
  "scripts": {
94
- "build": "tsup src/index.ts src/expo/index.ts src/repositories/sqlite/index.ts src/repositories/realm/index.ts --format esm,cjs --dts --clean",
116
+ "build": "tsup src/index.ts src/expo/index.ts src/expo/background.ts src/repositories/sqlite/index.ts src/repositories/realm/index.ts --format esm,cjs --dts --clean",
95
117
  "format": "prettier --write src test",
96
118
  "lint": "prettier --check src test",
97
119
  "test": "vitest run",
@@ -1,12 +0,0 @@
1
- import {
2
- defineExpoSwapBackgroundTask,
3
- registerExpoSwapBackgroundTask,
4
- unregisterExpoSwapBackgroundTask
5
- } from "./chunk-X3JNWDAR.js";
6
- import "./chunk-LWUXSE5N.js";
7
- import "./chunk-3RG5ZIWI.js";
8
- export {
9
- defineExpoSwapBackgroundTask,
10
- registerExpoSwapBackgroundTask,
11
- unregisterExpoSwapBackgroundTask
12
- };
@@ -1,10 +0,0 @@
1
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
- }) : x)(function(x) {
4
- if (typeof require !== "undefined") return require.apply(this, arguments);
5
- throw Error('Dynamic require of "' + x + '" is not supported');
6
- });
7
-
8
- export {
9
- __require
10
- };
@@ -1,261 +0,0 @@
1
- import {
2
- ArkadeSwaps,
3
- BoltzSwapProvider,
4
- isPendingReverseSwap,
5
- isPendingSubmarineSwap,
6
- isReverseClaimableStatus,
7
- isReverseFinalStatus,
8
- isSubmarineFinalStatus,
9
- isSubmarineSwapRefundable,
10
- logger
11
- } from "./chunk-LWUXSE5N.js";
12
- import {
13
- __require
14
- } from "./chunk-3RG5ZIWI.js";
15
-
16
- // src/expo/background.ts
17
- import { runTasks } from "@arkade-os/sdk/worker/expo";
18
- import {
19
- ExpoArkProvider,
20
- ExpoIndexerProvider
21
- } from "@arkade-os/sdk/adapters/expo";
22
-
23
- // src/expo/swapsPollProcessor.ts
24
- var SWAP_POLL_TASK_TYPE = "swap-poll";
25
- var swapsPollProcessor = {
26
- taskType: SWAP_POLL_TASK_TYPE,
27
- async execute(item, deps) {
28
- const {
29
- swapRepository,
30
- swapProvider,
31
- wallet,
32
- arkProvider,
33
- indexerProvider
34
- } = deps;
35
- const allSwaps = await swapRepository.getAllSwaps();
36
- const pendingSwaps = allSwaps.filter((swap) => {
37
- if (isPendingReverseSwap(swap))
38
- return !isReverseFinalStatus(swap.status);
39
- if (isPendingSubmarineSwap(swap))
40
- return !isSubmarineFinalStatus(swap.status);
41
- return false;
42
- });
43
- let polled = 0;
44
- let updated = 0;
45
- let claimed = 0;
46
- let refunded = 0;
47
- let errors = 0;
48
- const tempSwaps = new ArkadeSwaps({
49
- wallet,
50
- arkProvider,
51
- indexerProvider,
52
- swapProvider,
53
- swapManager: false,
54
- swapRepository
55
- });
56
- try {
57
- for (const swap of pendingSwaps) {
58
- try {
59
- const { status: currentStatus } = await swapProvider.getSwapStatus(swap.id);
60
- polled++;
61
- if (currentStatus !== swap.status) {
62
- await swapRepository.saveSwap({
63
- ...swap,
64
- status: currentStatus
65
- });
66
- updated++;
67
- }
68
- if (isPendingReverseSwap(swap) && isReverseClaimableStatus(currentStatus)) {
69
- if (!swap.preimage) {
70
- logger.warn(
71
- `[swap-poll] Skipping claim for ${swap.id}: no preimage`
72
- );
73
- continue;
74
- }
75
- try {
76
- await tempSwaps.claimVHTLC(swap);
77
- claimed++;
78
- } catch (claimError) {
79
- logger.error(
80
- `[swap-poll] Claim failed for ${swap.id}:`,
81
- claimError
82
- );
83
- errors++;
84
- }
85
- }
86
- const swapWithStatus = isPendingSubmarineSwap(swap) ? { ...swap, status: currentStatus } : null;
87
- if (isPendingSubmarineSwap(swap) && isSubmarineSwapRefundable(swapWithStatus)) {
88
- if (!swap.request.invoice && !swap.preimageHash) {
89
- logger.warn(
90
- `[swap-poll] Skipping refund for ${swap.id}: no invoice or preimageHash`
91
- );
92
- continue;
93
- }
94
- try {
95
- await tempSwaps.refundVHTLC(swapWithStatus);
96
- refunded++;
97
- } catch (refundError) {
98
- logger.error(
99
- `[swap-poll] Refund failed for ${swap.id}:`,
100
- refundError
101
- );
102
- errors++;
103
- }
104
- }
105
- } catch (swapError) {
106
- logger.error(
107
- `[swap-poll] Error processing swap ${swap.id}:`,
108
- swapError
109
- );
110
- errors++;
111
- }
112
- }
113
- } finally {
114
- await tempSwaps.dispose();
115
- }
116
- return {
117
- taskItemId: item.id,
118
- type: SWAP_POLL_TASK_TYPE,
119
- status: errors > 0 && polled === 0 ? "failed" : "success",
120
- data: { polled, updated, claimed, refunded, errors }
121
- };
122
- }
123
- };
124
-
125
- // src/expo/background.ts
126
- function requireTaskManager() {
127
- try {
128
- return __require("expo-task-manager");
129
- } catch {
130
- throw new Error(
131
- "expo-task-manager is required for background tasks. Install it with: npx expo install expo-task-manager"
132
- );
133
- }
134
- }
135
- function requireBackgroundTask() {
136
- try {
137
- return __require("expo-background-task");
138
- } catch {
139
- throw new Error(
140
- "expo-background-task is required for background tasks. Install it with: npx expo install expo-background-task"
141
- );
142
- }
143
- }
144
- function getRandomId() {
145
- return Math.random().toString(36).slice(2) + Date.now().toString(36);
146
- }
147
- function createBackgroundWalletShim(args) {
148
- const notImplemented = (method) => {
149
- throw new Error(
150
- `[boltz-swap] Background wallet shim: "${String(method)}" is not implemented`
151
- );
152
- };
153
- return {
154
- identity: args.identity,
155
- getAddress: args.getAddress,
156
- getBoardingAddress: async () => notImplemented("getBoardingAddress"),
157
- getBalance: async () => notImplemented("getBalance"),
158
- getVtxos: async () => notImplemented("getVtxos"),
159
- getBoardingUtxos: async () => notImplemented("getBoardingUtxos"),
160
- getTransactionHistory: async () => notImplemented("getTransactionHistory"),
161
- getContractManager: async () => notImplemented("getContractManager"),
162
- getDelegatorManager: async () => notImplemented("getDelegatorManager"),
163
- sendBitcoin: async () => notImplemented("sendBitcoin"),
164
- send: async () => notImplemented("send"),
165
- settle: async () => notImplemented("settle"),
166
- assetManager: new Proxy({}, {
167
- get: () => notImplemented("assetManager")
168
- })
169
- };
170
- }
171
- function defineExpoSwapBackgroundTask(taskName, options) {
172
- const TaskManager = requireTaskManager();
173
- const BackgroundTask = requireBackgroundTask();
174
- const { taskQueue, swapRepository, identityFactory } = options;
175
- TaskManager.defineTask(taskName, async () => {
176
- try {
177
- const config = await taskQueue.loadConfig();
178
- if (!config) {
179
- return BackgroundTask.BackgroundTaskResult.Success;
180
- }
181
- const identity = await identityFactory();
182
- const arkProvider = new ExpoArkProvider(config.arkServerUrl);
183
- const indexerProvider = new ExpoIndexerProvider(
184
- config.arkServerUrl
185
- );
186
- const swapProvider = new BoltzSwapProvider({
187
- network: config.network,
188
- apiUrl: config.boltzApiUrl
189
- });
190
- const wallet = createBackgroundWalletShim({
191
- identity,
192
- getAddress: async () => {
193
- const { ArkAddress } = await import("@arkade-os/sdk");
194
- const { hex } = await import("@scure/base");
195
- const info = await arkProvider.getInfo();
196
- const pubkey = await identity.xOnlyPublicKey();
197
- const serverPubKey = hex.decode(info.signerPubkey);
198
- const xOnlyServerPubKey = serverPubKey.length === 33 ? serverPubKey.slice(1) : serverPubKey;
199
- const hrp = info.network === "bitcoin" ? "ark" : "tark";
200
- return new ArkAddress(
201
- xOnlyServerPubKey,
202
- pubkey,
203
- hrp
204
- ).encode();
205
- }
206
- });
207
- const deps = {
208
- swapRepository,
209
- swapProvider,
210
- arkProvider,
211
- indexerProvider,
212
- identity,
213
- wallet
214
- };
215
- await runTasks(taskQueue, [swapsPollProcessor], deps);
216
- const results = await taskQueue.getResults();
217
- if (results.length > 0) {
218
- await taskQueue.acknowledgeResults(
219
- results.map((r) => r.id)
220
- );
221
- }
222
- const existing = await taskQueue.getTasks(SWAP_POLL_TASK_TYPE);
223
- if (existing.length === 0) {
224
- const task = {
225
- id: getRandomId(),
226
- type: SWAP_POLL_TASK_TYPE,
227
- data: {},
228
- createdAt: Date.now()
229
- };
230
- await taskQueue.addTask(task);
231
- }
232
- return BackgroundTask.BackgroundTaskResult.Success;
233
- } catch (error) {
234
- console.error(
235
- "[boltz-swap] Background task failed:",
236
- error instanceof Error ? error.message : error
237
- );
238
- return BackgroundTask.BackgroundTaskResult.Failed;
239
- }
240
- });
241
- }
242
- async function registerExpoSwapBackgroundTask(taskName, options) {
243
- const BackgroundTask = requireBackgroundTask();
244
- await BackgroundTask.registerTaskAsync(taskName, {
245
- // expo-background-task expects minutes:
246
- // https://docs.expo.dev/versions/latest/sdk/background-task/#backgroundtaskoptions
247
- minimumInterval: options?.minimumInterval ?? 15
248
- });
249
- }
250
- async function unregisterExpoSwapBackgroundTask(taskName) {
251
- const BackgroundTask = requireBackgroundTask();
252
- await BackgroundTask.unregisterTaskAsync(taskName);
253
- }
254
-
255
- export {
256
- SWAP_POLL_TASK_TYPE,
257
- swapsPollProcessor,
258
- defineExpoSwapBackgroundTask,
259
- registerExpoSwapBackgroundTask,
260
- unregisterExpoSwapBackgroundTask
261
- };