@arkade-os/sdk 0.1.1 → 0.1.3

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
@@ -117,18 +117,26 @@ new Worker().start()
117
117
  ```typescript
118
118
  // specify the path to the service worker file
119
119
  // this will automatically register the service worker
120
- const wallet = await ServiceWorkerWallet.create('/service-worker.js')
120
+ const serviceWorker = await setupServiceWorker('/service-worker.js')
121
+ const wallet = new ServiceWorkerWallet(serviceWorker)
121
122
 
122
123
  // initialize the wallet
123
124
  await wallet.init({
124
125
  network: 'mutinynet', // 'bitcoin', 'testnet', 'regtest', 'signet' or 'mutinynet'
125
- identity: identity,
126
+ privateKey: 'your_private_key_hex',
126
127
  // Esplora API, can be left empty mempool.space API will be used
127
128
  esploraUrl: 'https://mutinynet.com/api',
128
129
  // OPTIONAL Ark Server connection information
129
130
  arkServerUrl: 'https://mutinynet.arkade.sh',
130
131
  arkServerPublicKey: 'fa73c6e4876ffb2dfc961d763cca9abc73d4b88efcb8f5e7ff92dc55e9aa553d'
131
132
  })
133
+
134
+ // check service worker status
135
+ const status = await wallet.getStatus()
136
+ console.log('Service worker status:', status.walletInitialized)
137
+
138
+ // clear wallet data stored in the service worker memory
139
+ await wallet.clear()
132
140
  ```
133
141
 
134
142
  ## API Reference
@@ -149,6 +157,10 @@ interface WalletConfig {
149
157
  arkServerUrl?: string;
150
158
  /** Ark server public key (optional) */
151
159
  arkServerPublicKey?: string;
160
+ /** Optional boarding timelock configuration */
161
+ boardingTimelock?: RelativeTimelock;
162
+ /** Optional exit timelock configuration */
163
+ exitTimelock?: RelativeTimelock;
152
164
  }
153
165
  ```
154
166
 
@@ -175,7 +187,9 @@ interface IWallet {
175
187
  total: number;
176
188
  settled: number;
177
189
  pending: number;
190
+ swept: number;
178
191
  };
192
+ total: number;
179
193
  }>;
180
194
 
181
195
  /** Send bitcoin (on-chain or off-chain) */
@@ -183,7 +197,8 @@ interface IWallet {
183
197
  address: string;
184
198
  amount: number;
185
199
  feeRate?: number;
186
- }, onchain?: boolean): Promise<string>;
200
+ memo?: string;
201
+ }, zeroFee?: boolean): Promise<string>;
187
202
 
188
203
  /** Get virtual UTXOs */
189
204
  getVtxos(): Promise<VirtualCoin[]>;
@@ -228,6 +243,8 @@ interface VirtualCoin {
228
243
  virtualStatus: {
229
244
  state: 'pending' | 'settled';
230
245
  };
246
+ spentBy?: string;
247
+ createdAt: Date;
231
248
  }
232
249
 
233
250
  /** Boarding UTXO */
package/dist/cjs/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.IndexedDBVtxoRepository = exports.networks = exports.ArkNoteData = exports.ArkNote = exports.createVirtualTx = exports.CONDITION_WITNESS_KEY_PREFIX = exports.addConditionWitness = exports.CLTVMultisigTapscript = exports.ConditionMultisigTapscript = exports.ConditionCSVMultisigTapscript = exports.CSVMultisigTapscript = exports.MultisigTapscript = exports.decodeTapscript = exports.Response = exports.Request = exports.Worker = exports.TxType = exports.VHTLC = exports.VtxoScript = exports.DefaultVtxo = exports.ArkAddress = exports.RestArkProvider = exports.EsploraProvider = exports.ESPLORA_URL = exports.InMemoryKey = exports.ServiceWorkerWallet = exports.Wallet = void 0;
3
+ exports.IndexedDBVtxoRepository = exports.networks = exports.ArkNoteData = exports.ArkNote = exports.createVirtualTx = exports.CONDITION_WITNESS_KEY_PREFIX = exports.addConditionWitness = exports.CLTVMultisigTapscript = exports.ConditionMultisigTapscript = exports.ConditionCSVMultisigTapscript = exports.CSVMultisigTapscript = exports.MultisigTapscript = exports.decodeTapscript = exports.Response = exports.Request = exports.ServiceWorkerWallet = exports.Worker = exports.setupServiceWorker = exports.TxType = exports.VHTLC = exports.VtxoScript = exports.DefaultVtxo = exports.ArkAddress = exports.RestArkProvider = exports.EsploraProvider = exports.ESPLORA_URL = exports.InMemoryKey = exports.Wallet = void 0;
4
4
  const inMemoryKey_1 = require("./identity/inMemoryKey");
5
5
  Object.defineProperty(exports, "InMemoryKey", { enumerable: true, get: function () { return inMemoryKey_1.InMemoryKey; } });
6
6
  const address_1 = require("./script/address");
@@ -17,6 +17,8 @@ const wallet_1 = require("./wallet/wallet");
17
17
  Object.defineProperty(exports, "Wallet", { enumerable: true, get: function () { return wallet_1.Wallet; } });
18
18
  const wallet_2 = require("./wallet/serviceWorker/wallet");
19
19
  Object.defineProperty(exports, "ServiceWorkerWallet", { enumerable: true, get: function () { return wallet_2.ServiceWorkerWallet; } });
20
+ const utils_1 = require("./wallet/serviceWorker/utils");
21
+ Object.defineProperty(exports, "setupServiceWorker", { enumerable: true, get: function () { return utils_1.setupServiceWorker; } });
20
22
  const worker_1 = require("./wallet/serviceWorker/worker");
21
23
  Object.defineProperty(exports, "Worker", { enumerable: true, get: function () { return worker_1.Worker; } });
22
24
  const request_1 = require("./wallet/serviceWorker/request");
@@ -368,6 +368,12 @@ class RestArkProvider {
368
368
  }
369
369
  }
370
370
  catch (error) {
371
+ // ignore timeout errors, they're expected when the server is not sending anything for 5 min
372
+ // these timeouts are set by builtin fetch function
373
+ if (isFetchTimeoutError(error)) {
374
+ console.debug("Timeout error ignored");
375
+ continue;
376
+ }
371
377
  console.error("Address subscription error:", error);
372
378
  throw error;
373
379
  }
@@ -545,3 +551,13 @@ function convertVtxo(vtxo) {
545
551
  createdAt: new Date(vtxo.createdAt * 1000),
546
552
  };
547
553
  }
554
+ function isFetchTimeoutError(err) {
555
+ const checkError = (error) => {
556
+ return (error instanceof Error &&
557
+ (error.name === "HeadersTimeoutError" ||
558
+ error.name === "BodyTimeoutError" ||
559
+ error.code === "UND_ERR_HEADERS_TIMEOUT" ||
560
+ error.code === "UND_ERR_BODY_TIMEOUT"));
561
+ };
562
+ return checkError(err) || checkError(err.cause);
563
+ }
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setupServiceWorker = setupServiceWorker;
4
+ async function setupServiceWorker(path) {
5
+ // check if service workers are supported
6
+ if (!("serviceWorker" in navigator)) {
7
+ throw new Error("Service workers are not supported in this browser");
8
+ }
9
+ // register service worker
10
+ const registration = await navigator.serviceWorker.register(path);
11
+ // force update to ensure the service worker is active
12
+ registration.update();
13
+ const serviceWorker = registration.active || registration.waiting || registration.installing;
14
+ if (!serviceWorker) {
15
+ throw new Error("Failed to get service worker instance");
16
+ }
17
+ // wait for the service worker to be ready
18
+ if (serviceWorker.state !== "activated") {
19
+ await new Promise((resolve) => {
20
+ if (!serviceWorker)
21
+ return resolve();
22
+ serviceWorker.addEventListener("activate", () => resolve());
23
+ });
24
+ }
25
+ return serviceWorker;
26
+ }
@@ -11,15 +11,8 @@ class UnexpectedResponseError extends Error {
11
11
  }
12
12
  // ServiceWorkerWallet is a wallet that uses a service worker as "backend" to handle the wallet logic
13
13
  class ServiceWorkerWallet {
14
- static async create(svcWorkerPath) {
15
- try {
16
- const wallet = new ServiceWorkerWallet();
17
- await wallet.setupServiceWorker(svcWorkerPath);
18
- return wallet;
19
- }
20
- catch (error) {
21
- throw new Error(`Failed to initialize service worker wallet: ${error}`);
22
- }
14
+ constructor(serviceWorker) {
15
+ this.serviceWorker = serviceWorker;
23
16
  }
24
17
  async getStatus() {
25
18
  const message = {
@@ -64,64 +57,8 @@ class ServiceWorkerWallet {
64
57
  };
65
58
  await this.sendMessage(message);
66
59
  }
67
- // register the service worker
68
- async setupServiceWorker(path) {
69
- // check if service workers are supported
70
- if (!("serviceWorker" in navigator)) {
71
- throw new Error("Service workers are not supported in this browser");
72
- }
73
- try {
74
- // check for existing registration
75
- const existingRegistration = await navigator.serviceWorker.getRegistration(path);
76
- let registration;
77
- if (existingRegistration) {
78
- registration = existingRegistration;
79
- // Force unregister and re-register to ensure we get the latest version
80
- await existingRegistration.unregister();
81
- }
82
- registration = await navigator.serviceWorker.register(path);
83
- // Handle updates
84
- registration.addEventListener("updatefound", () => {
85
- const newWorker = registration.installing;
86
- if (!newWorker)
87
- return;
88
- newWorker.addEventListener("statechange", () => {
89
- if (newWorker.state === "activated" &&
90
- navigator.serviceWorker.controller) {
91
- console.info("Service worker activated, reloading...");
92
- window.location.reload();
93
- }
94
- });
95
- });
96
- const sw = registration.active ||
97
- registration.waiting ||
98
- registration.installing;
99
- if (!sw) {
100
- throw new Error("Failed to get service worker instance");
101
- }
102
- this.serviceWorker = sw;
103
- // wait for the service worker to be ready
104
- if (this.serviceWorker?.state !== "activated") {
105
- await new Promise((resolve) => {
106
- if (!this.serviceWorker)
107
- return resolve();
108
- this.serviceWorker.addEventListener("statechange", () => {
109
- if (this.serviceWorker?.state === "activated") {
110
- resolve();
111
- }
112
- });
113
- });
114
- }
115
- }
116
- catch (error) {
117
- throw new Error(`Failed to setup service worker: ${error}`);
118
- }
119
- }
120
60
  // send a message and wait for a response
121
61
  async sendMessage(message) {
122
- if (!this.serviceWorker) {
123
- throw new Error("Service worker not initialized");
124
- }
125
62
  return new Promise((resolve, reject) => {
126
63
  const messageHandler = (event) => {
127
64
  const response = event.data;
@@ -141,12 +78,7 @@ class ServiceWorkerWallet {
141
78
  }
142
79
  };
143
80
  navigator.serviceWorker.addEventListener("message", messageHandler);
144
- if (this.serviceWorker) {
145
- this.serviceWorker.postMessage(message);
146
- }
147
- else {
148
- reject(new Error("Service worker not initialized"));
149
- }
81
+ this.serviceWorker.postMessage(message);
150
82
  });
151
83
  }
152
84
  async getAddress() {
@@ -293,12 +225,7 @@ class ServiceWorkerWallet {
293
225
  }
294
226
  };
295
227
  navigator.serviceWorker.addEventListener("message", messageHandler);
296
- if (this.serviceWorker) {
297
- this.serviceWorker.postMessage(message);
298
- }
299
- else {
300
- reject(new Error("Service worker not initialized"));
301
- }
228
+ this.serviceWorker.postMessage(message);
302
229
  });
303
230
  }
304
231
  catch (error) {
package/dist/esm/index.js CHANGED
@@ -6,6 +6,7 @@ import { VtxoScript } from './script/base.js';
6
6
  import { TxType, } from './wallet/index.js';
7
7
  import { Wallet } from './wallet/wallet.js';
8
8
  import { ServiceWorkerWallet } from './wallet/serviceWorker/wallet.js';
9
+ import { setupServiceWorker } from './wallet/serviceWorker/utils.js';
9
10
  import { Worker } from './wallet/serviceWorker/worker.js';
10
11
  import { Request } from './wallet/serviceWorker/request.js';
11
12
  import { Response } from './wallet/serviceWorker/response.js';
@@ -16,9 +17,7 @@ import { addConditionWitness, CONDITION_WITNESS_KEY_PREFIX, createVirtualTx, } f
16
17
  import { ArkNote, ArkNoteData } from './arknote/index.js';
17
18
  import { IndexedDBVtxoRepository } from './wallet/serviceWorker/db/vtxo/idb.js';
18
19
  import { networks } from './networks.js';
19
- export {
20
- // Classes
21
- Wallet, ServiceWorkerWallet, InMemoryKey,
20
+ export { Wallet, InMemoryKey,
22
21
  // Providers
23
22
  ESPLORA_URL, EsploraProvider, RestArkProvider,
24
23
  // Script-related
@@ -26,7 +25,7 @@ ArkAddress, DefaultVtxo, VtxoScript, VHTLC,
26
25
  // Enums
27
26
  TxType,
28
27
  // Service Worker
29
- Worker, Request, Response,
28
+ setupServiceWorker, Worker, ServiceWorkerWallet, Request, Response,
30
29
  // Tapscript
31
30
  decodeTapscript, MultisigTapscript, CSVMultisigTapscript, ConditionCSVMultisigTapscript, ConditionMultisigTapscript, CLTVMultisigTapscript,
32
31
  // Utils
@@ -365,6 +365,12 @@ export class RestArkProvider {
365
365
  }
366
366
  }
367
367
  catch (error) {
368
+ // ignore timeout errors, they're expected when the server is not sending anything for 5 min
369
+ // these timeouts are set by builtin fetch function
370
+ if (isFetchTimeoutError(error)) {
371
+ console.debug("Timeout error ignored");
372
+ continue;
373
+ }
368
374
  console.error("Address subscription error:", error);
369
375
  throw error;
370
376
  }
@@ -541,3 +547,13 @@ function convertVtxo(vtxo) {
541
547
  createdAt: new Date(vtxo.createdAt * 1000),
542
548
  };
543
549
  }
550
+ function isFetchTimeoutError(err) {
551
+ const checkError = (error) => {
552
+ return (error instanceof Error &&
553
+ (error.name === "HeadersTimeoutError" ||
554
+ error.name === "BodyTimeoutError" ||
555
+ error.code === "UND_ERR_HEADERS_TIMEOUT" ||
556
+ error.code === "UND_ERR_BODY_TIMEOUT"));
557
+ };
558
+ return checkError(err) || checkError(err.cause);
559
+ }
@@ -0,0 +1,23 @@
1
+ export async function setupServiceWorker(path) {
2
+ // check if service workers are supported
3
+ if (!("serviceWorker" in navigator)) {
4
+ throw new Error("Service workers are not supported in this browser");
5
+ }
6
+ // register service worker
7
+ const registration = await navigator.serviceWorker.register(path);
8
+ // force update to ensure the service worker is active
9
+ registration.update();
10
+ const serviceWorker = registration.active || registration.waiting || registration.installing;
11
+ if (!serviceWorker) {
12
+ throw new Error("Failed to get service worker instance");
13
+ }
14
+ // wait for the service worker to be ready
15
+ if (serviceWorker.state !== "activated") {
16
+ await new Promise((resolve) => {
17
+ if (!serviceWorker)
18
+ return resolve();
19
+ serviceWorker.addEventListener("activate", () => resolve());
20
+ });
21
+ }
22
+ return serviceWorker;
23
+ }
@@ -8,15 +8,8 @@ class UnexpectedResponseError extends Error {
8
8
  }
9
9
  // ServiceWorkerWallet is a wallet that uses a service worker as "backend" to handle the wallet logic
10
10
  export class ServiceWorkerWallet {
11
- static async create(svcWorkerPath) {
12
- try {
13
- const wallet = new ServiceWorkerWallet();
14
- await wallet.setupServiceWorker(svcWorkerPath);
15
- return wallet;
16
- }
17
- catch (error) {
18
- throw new Error(`Failed to initialize service worker wallet: ${error}`);
19
- }
11
+ constructor(serviceWorker) {
12
+ this.serviceWorker = serviceWorker;
20
13
  }
21
14
  async getStatus() {
22
15
  const message = {
@@ -61,64 +54,8 @@ export class ServiceWorkerWallet {
61
54
  };
62
55
  await this.sendMessage(message);
63
56
  }
64
- // register the service worker
65
- async setupServiceWorker(path) {
66
- // check if service workers are supported
67
- if (!("serviceWorker" in navigator)) {
68
- throw new Error("Service workers are not supported in this browser");
69
- }
70
- try {
71
- // check for existing registration
72
- const existingRegistration = await navigator.serviceWorker.getRegistration(path);
73
- let registration;
74
- if (existingRegistration) {
75
- registration = existingRegistration;
76
- // Force unregister and re-register to ensure we get the latest version
77
- await existingRegistration.unregister();
78
- }
79
- registration = await navigator.serviceWorker.register(path);
80
- // Handle updates
81
- registration.addEventListener("updatefound", () => {
82
- const newWorker = registration.installing;
83
- if (!newWorker)
84
- return;
85
- newWorker.addEventListener("statechange", () => {
86
- if (newWorker.state === "activated" &&
87
- navigator.serviceWorker.controller) {
88
- console.info("Service worker activated, reloading...");
89
- window.location.reload();
90
- }
91
- });
92
- });
93
- const sw = registration.active ||
94
- registration.waiting ||
95
- registration.installing;
96
- if (!sw) {
97
- throw new Error("Failed to get service worker instance");
98
- }
99
- this.serviceWorker = sw;
100
- // wait for the service worker to be ready
101
- if (this.serviceWorker?.state !== "activated") {
102
- await new Promise((resolve) => {
103
- if (!this.serviceWorker)
104
- return resolve();
105
- this.serviceWorker.addEventListener("statechange", () => {
106
- if (this.serviceWorker?.state === "activated") {
107
- resolve();
108
- }
109
- });
110
- });
111
- }
112
- }
113
- catch (error) {
114
- throw new Error(`Failed to setup service worker: ${error}`);
115
- }
116
- }
117
57
  // send a message and wait for a response
118
58
  async sendMessage(message) {
119
- if (!this.serviceWorker) {
120
- throw new Error("Service worker not initialized");
121
- }
122
59
  return new Promise((resolve, reject) => {
123
60
  const messageHandler = (event) => {
124
61
  const response = event.data;
@@ -138,12 +75,7 @@ export class ServiceWorkerWallet {
138
75
  }
139
76
  };
140
77
  navigator.serviceWorker.addEventListener("message", messageHandler);
141
- if (this.serviceWorker) {
142
- this.serviceWorker.postMessage(message);
143
- }
144
- else {
145
- reject(new Error("Service worker not initialized"));
146
- }
78
+ this.serviceWorker.postMessage(message);
147
79
  });
148
80
  }
149
81
  async getAddress() {
@@ -290,12 +222,7 @@ export class ServiceWorkerWallet {
290
222
  }
291
223
  };
292
224
  navigator.serviceWorker.addEventListener("message", messageHandler);
293
- if (this.serviceWorker) {
294
- this.serviceWorker.postMessage(message);
295
- }
296
- else {
297
- reject(new Error("Service worker not initialized"));
298
- }
225
+ this.serviceWorker.postMessage(message);
299
226
  });
300
227
  }
301
228
  catch (error) {
@@ -7,6 +7,7 @@ import { VtxoScript } from "./script/base";
7
7
  import { TxType, IWallet, WalletConfig, ArkTransaction, Coin, ExtendedCoin, ExtendedVirtualCoin, WalletBalance, SendBitcoinParams, Recipient, SettleParams, VtxoTaprootAddress, AddressInfo, TapscriptInfo, Status, VirtualStatus, Outpoint, VirtualCoin, TxKey, Addresses } from "./wallet/index";
8
8
  import { Wallet } from "./wallet/wallet";
9
9
  import { ServiceWorkerWallet } from "./wallet/serviceWorker/wallet";
10
+ import { setupServiceWorker } from "./wallet/serviceWorker/utils";
10
11
  import { Worker } from "./wallet/serviceWorker/worker";
11
12
  import { Request } from "./wallet/serviceWorker/request";
12
13
  import { Response } from "./wallet/serviceWorker/response";
@@ -18,5 +19,5 @@ import { ArkNote, ArkNoteData } from "./arknote";
18
19
  import { IndexedDBVtxoRepository } from "./wallet/serviceWorker/db/vtxo/idb";
19
20
  import { VtxoRepository } from "./wallet/serviceWorker/db/vtxo";
20
21
  import { networks } from "./networks";
21
- export { Wallet, ServiceWorkerWallet, InMemoryKey, ESPLORA_URL, EsploraProvider, RestArkProvider, ArkAddress, DefaultVtxo, VtxoScript, VHTLC, TxType, Worker, Request, Response, decodeTapscript, MultisigTapscript, CSVMultisigTapscript, ConditionCSVMultisigTapscript, ConditionMultisigTapscript, CLTVMultisigTapscript, addConditionWitness, CONDITION_WITNESS_KEY_PREFIX, createVirtualTx, ArkNote, ArkNoteData, networks, IndexedDBVtxoRepository, };
22
+ export { Wallet, InMemoryKey, ESPLORA_URL, EsploraProvider, RestArkProvider, ArkAddress, DefaultVtxo, VtxoScript, VHTLC, TxType, setupServiceWorker, Worker, ServiceWorkerWallet, Request, Response, decodeTapscript, MultisigTapscript, CSVMultisigTapscript, ConditionCSVMultisigTapscript, ConditionMultisigTapscript, CLTVMultisigTapscript, addConditionWitness, CONDITION_WITNESS_KEY_PREFIX, createVirtualTx, ArkNote, ArkNoteData, networks, IndexedDBVtxoRepository, };
22
23
  export type { Identity, IWallet, WalletConfig, ArkTransaction, Coin, ExtendedCoin, ExtendedVirtualCoin, WalletBalance, SendBitcoinParams, Recipient, SettleParams, VtxoTaprootAddress, AddressInfo, Addresses, TapscriptInfo, Status, VirtualStatus, Outpoint, VirtualCoin, TxKey, TapscriptType, VtxoRepository, };
@@ -0,0 +1 @@
1
+ export declare function setupServiceWorker(path: string): Promise<ServiceWorker>;
@@ -2,14 +2,13 @@ import { IWallet, WalletBalance, SendBitcoinParams, SettleParams, AddressInfo, C
2
2
  import { Response } from "./response";
3
3
  import { SettlementEvent } from "../../providers/ark";
4
4
  export declare class ServiceWorkerWallet implements IWallet {
5
- private serviceWorker?;
6
- static create(svcWorkerPath: string): Promise<ServiceWorkerWallet>;
5
+ readonly serviceWorker: ServiceWorker;
6
+ constructor(serviceWorker: ServiceWorker);
7
7
  getStatus(): Promise<Response.WalletStatus["status"]>;
8
8
  init(config: Omit<WalletConfig, "identity"> & {
9
9
  privateKey: string;
10
10
  }, failIfInitialized?: boolean): Promise<void>;
11
11
  clear(): Promise<void>;
12
- private setupServiceWorker;
13
12
  private sendMessage;
14
13
  getAddress(): Promise<Addresses>;
15
14
  getAddressInfo(): Promise<AddressInfo>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arkade-os/sdk",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Bitcoin wallet SDK with Taproot and Ark integration",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",