@arkade-os/sdk 0.1.0 → 0.1.2

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
+ }
@@ -43,16 +43,25 @@ function addConditionWitness(inIndex, tx, witness) {
43
43
  });
44
44
  }
45
45
  function createVirtualTx(inputs, outputs) {
46
- let lockTime;
46
+ let lockTime = 0n;
47
47
  for (const input of inputs) {
48
48
  const tapscript = (0, tapscript_1.decodeTapscript)((0, base_1.scriptFromTapLeafScript)(input.tapLeafScript));
49
49
  if (tapscript_1.CLTVMultisigTapscript.is(tapscript)) {
50
- lockTime = Number(tapscript.params.absoluteTimelock);
50
+ if (lockTime !== 0n) {
51
+ // if a locktime is already set, check if the new locktime is in the same unit
52
+ if (isSeconds(lockTime) !==
53
+ isSeconds(tapscript.params.absoluteTimelock)) {
54
+ throw new Error("cannot mix seconds and blocks locktime");
55
+ }
56
+ }
57
+ if (tapscript.params.absoluteTimelock > lockTime) {
58
+ lockTime = tapscript.params.absoluteTimelock;
59
+ }
51
60
  }
52
61
  }
53
62
  const tx = new btc_signer_1.Transaction({
54
63
  allowUnknown: true,
55
- lockTime,
64
+ lockTime: Number(lockTime),
56
65
  });
57
66
  for (const [i, input] of inputs.entries()) {
58
67
  tx.addInput({
@@ -122,3 +131,7 @@ function encodeCompactSizeUint(value) {
122
131
  return buffer;
123
132
  }
124
133
  }
134
+ const nLocktimeMinSeconds = 500000000n;
135
+ function isSeconds(locktime) {
136
+ return locktime >= nLocktimeMinSeconds;
137
+ }
@@ -0,0 +1,48 @@
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
+ // check for existing registration
10
+ const existingRegistration = await navigator.serviceWorker.getRegistration(path);
11
+ let registration;
12
+ if (existingRegistration) {
13
+ registration = existingRegistration;
14
+ // Force unregister and re-register to ensure we get the latest version
15
+ await existingRegistration.unregister();
16
+ }
17
+ registration = await navigator.serviceWorker.register(path);
18
+ // Handle updates
19
+ registration.addEventListener("updatefound", () => {
20
+ const newWorker = registration.installing;
21
+ if (!newWorker)
22
+ return;
23
+ newWorker.addEventListener("statechange", () => {
24
+ if (newWorker.state === "activated" &&
25
+ navigator.serviceWorker.controller) {
26
+ console.info("Service worker activated, reloading...");
27
+ window.location.reload();
28
+ }
29
+ });
30
+ });
31
+ const serviceWorker = registration.active || registration.waiting || registration.installing;
32
+ if (!serviceWorker) {
33
+ throw new Error("Failed to get service worker instance");
34
+ }
35
+ // wait for the service worker to be ready
36
+ if (serviceWorker.state !== "activated") {
37
+ await new Promise((resolve) => {
38
+ if (!serviceWorker)
39
+ return resolve();
40
+ serviceWorker.addEventListener("statechange", () => {
41
+ if (serviceWorker.state === "activated") {
42
+ resolve();
43
+ }
44
+ });
45
+ });
46
+ }
47
+ return serviceWorker;
48
+ }
@@ -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,67 +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
- console.info("@arklabs/wallet-sdk: Service worker auto-update...");
86
- const newWorker = registration.installing;
87
- if (!newWorker)
88
- return;
89
- newWorker.addEventListener("statechange", () => {
90
- if (newWorker.state === "installed" &&
91
- navigator.serviceWorker.controller) {
92
- console.info("@arklabs/wallet-sdk: Service worker updated, reloading...");
93
- window.location.reload();
94
- }
95
- });
96
- });
97
- // Check for updates
98
- await registration.update();
99
- const sw = registration.active ||
100
- registration.waiting ||
101
- registration.installing;
102
- if (!sw) {
103
- throw new Error("Failed to get service worker instance");
104
- }
105
- this.serviceWorker = sw;
106
- // wait for the service worker to be ready
107
- if (this.serviceWorker?.state !== "activated") {
108
- await new Promise((resolve) => {
109
- if (!this.serviceWorker)
110
- return resolve();
111
- this.serviceWorker.addEventListener("statechange", () => {
112
- if (this.serviceWorker?.state === "activated") {
113
- resolve();
114
- }
115
- });
116
- });
117
- }
118
- }
119
- catch (error) {
120
- throw new Error(`Failed to setup service worker: ${error}`);
121
- }
122
- }
123
60
  // send a message and wait for a response
124
61
  async sendMessage(message) {
125
- if (!this.serviceWorker) {
126
- throw new Error("Service worker not initialized");
127
- }
128
62
  return new Promise((resolve, reject) => {
129
63
  const messageHandler = (event) => {
130
64
  const response = event.data;
@@ -144,12 +78,7 @@ class ServiceWorkerWallet {
144
78
  }
145
79
  };
146
80
  navigator.serviceWorker.addEventListener("message", messageHandler);
147
- if (this.serviceWorker) {
148
- this.serviceWorker.postMessage(message);
149
- }
150
- else {
151
- reject(new Error("Service worker not initialized"));
152
- }
81
+ this.serviceWorker.postMessage(message);
153
82
  });
154
83
  }
155
84
  async getAddress() {
@@ -296,12 +225,7 @@ class ServiceWorkerWallet {
296
225
  }
297
226
  };
298
227
  navigator.serviceWorker.addEventListener("message", messageHandler);
299
- if (this.serviceWorker) {
300
- this.serviceWorker.postMessage(message);
301
- }
302
- else {
303
- reject(new Error("Service worker not initialized"));
304
- }
228
+ this.serviceWorker.postMessage(message);
305
229
  });
306
230
  }
307
231
  catch (error) {
@@ -17,10 +17,20 @@ class Worker {
17
17
  this.vtxoRepository = vtxoRepository;
18
18
  this.messageCallback = messageCallback;
19
19
  }
20
- async start() {
20
+ async start(withServiceWorkerUpdate = true) {
21
21
  self.addEventListener("message", async (event) => {
22
22
  await this.handleMessage(event);
23
23
  });
24
+ if (withServiceWorkerUpdate) {
25
+ // activate service worker immediately
26
+ self.addEventListener("install", () => {
27
+ self.skipWaiting();
28
+ });
29
+ // take control of clients immediately
30
+ self.addEventListener("activate", () => {
31
+ self.clients.claim();
32
+ });
33
+ }
24
34
  }
25
35
  async clear() {
26
36
  if (this.vtxoSubscription) {
@@ -369,7 +369,6 @@ class Wallet {
369
369
  amount: BigInt(input.value),
370
370
  },
371
371
  tapInternalKey: this.onchainP2TR.tapInternalKey,
372
- tapMerkleRoot: this.onchainP2TR.tapMerkleRoot,
373
372
  });
374
373
  }
375
374
  // Add payment output
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
+ }
@@ -37,16 +37,25 @@ export function addConditionWitness(inIndex, tx, witness) {
37
37
  });
38
38
  }
39
39
  export function createVirtualTx(inputs, outputs) {
40
- let lockTime;
40
+ let lockTime = 0n;
41
41
  for (const input of inputs) {
42
42
  const tapscript = decodeTapscript(scriptFromTapLeafScript(input.tapLeafScript));
43
43
  if (CLTVMultisigTapscript.is(tapscript)) {
44
- lockTime = Number(tapscript.params.absoluteTimelock);
44
+ if (lockTime !== 0n) {
45
+ // if a locktime is already set, check if the new locktime is in the same unit
46
+ if (isSeconds(lockTime) !==
47
+ isSeconds(tapscript.params.absoluteTimelock)) {
48
+ throw new Error("cannot mix seconds and blocks locktime");
49
+ }
50
+ }
51
+ if (tapscript.params.absoluteTimelock > lockTime) {
52
+ lockTime = tapscript.params.absoluteTimelock;
53
+ }
45
54
  }
46
55
  }
47
56
  const tx = new Transaction({
48
57
  allowUnknown: true,
49
- lockTime,
58
+ lockTime: Number(lockTime),
50
59
  });
51
60
  for (const [i, input] of inputs.entries()) {
52
61
  tx.addInput({
@@ -116,3 +125,7 @@ function encodeCompactSizeUint(value) {
116
125
  return buffer;
117
126
  }
118
127
  }
128
+ const nLocktimeMinSeconds = 500000000n;
129
+ function isSeconds(locktime) {
130
+ return locktime >= nLocktimeMinSeconds;
131
+ }
@@ -0,0 +1,45 @@
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
+ // check for existing registration
7
+ const existingRegistration = await navigator.serviceWorker.getRegistration(path);
8
+ let registration;
9
+ if (existingRegistration) {
10
+ registration = existingRegistration;
11
+ // Force unregister and re-register to ensure we get the latest version
12
+ await existingRegistration.unregister();
13
+ }
14
+ registration = await navigator.serviceWorker.register(path);
15
+ // Handle updates
16
+ registration.addEventListener("updatefound", () => {
17
+ const newWorker = registration.installing;
18
+ if (!newWorker)
19
+ return;
20
+ newWorker.addEventListener("statechange", () => {
21
+ if (newWorker.state === "activated" &&
22
+ navigator.serviceWorker.controller) {
23
+ console.info("Service worker activated, reloading...");
24
+ window.location.reload();
25
+ }
26
+ });
27
+ });
28
+ const serviceWorker = registration.active || registration.waiting || registration.installing;
29
+ if (!serviceWorker) {
30
+ throw new Error("Failed to get service worker instance");
31
+ }
32
+ // wait for the service worker to be ready
33
+ if (serviceWorker.state !== "activated") {
34
+ await new Promise((resolve) => {
35
+ if (!serviceWorker)
36
+ return resolve();
37
+ serviceWorker.addEventListener("statechange", () => {
38
+ if (serviceWorker.state === "activated") {
39
+ resolve();
40
+ }
41
+ });
42
+ });
43
+ }
44
+ return serviceWorker;
45
+ }
@@ -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,67 +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
- console.info("@arklabs/wallet-sdk: Service worker auto-update...");
83
- const newWorker = registration.installing;
84
- if (!newWorker)
85
- return;
86
- newWorker.addEventListener("statechange", () => {
87
- if (newWorker.state === "installed" &&
88
- navigator.serviceWorker.controller) {
89
- console.info("@arklabs/wallet-sdk: Service worker updated, reloading...");
90
- window.location.reload();
91
- }
92
- });
93
- });
94
- // Check for updates
95
- await registration.update();
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
57
  // send a message and wait for a response
121
58
  async sendMessage(message) {
122
- if (!this.serviceWorker) {
123
- throw new Error("Service worker not initialized");
124
- }
125
59
  return new Promise((resolve, reject) => {
126
60
  const messageHandler = (event) => {
127
61
  const response = event.data;
@@ -141,12 +75,7 @@ export class ServiceWorkerWallet {
141
75
  }
142
76
  };
143
77
  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
- }
78
+ this.serviceWorker.postMessage(message);
150
79
  });
151
80
  }
152
81
  async getAddress() {
@@ -293,12 +222,7 @@ export class ServiceWorkerWallet {
293
222
  }
294
223
  };
295
224
  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
- }
225
+ this.serviceWorker.postMessage(message);
302
226
  });
303
227
  }
304
228
  catch (error) {
@@ -14,10 +14,20 @@ export class Worker {
14
14
  this.vtxoRepository = vtxoRepository;
15
15
  this.messageCallback = messageCallback;
16
16
  }
17
- async start() {
17
+ async start(withServiceWorkerUpdate = true) {
18
18
  self.addEventListener("message", async (event) => {
19
19
  await this.handleMessage(event);
20
20
  });
21
+ if (withServiceWorkerUpdate) {
22
+ // activate service worker immediately
23
+ self.addEventListener("install", () => {
24
+ self.skipWaiting();
25
+ });
26
+ // take control of clients immediately
27
+ self.addEventListener("activate", () => {
28
+ self.clients.claim();
29
+ });
30
+ }
21
31
  }
22
32
  async clear() {
23
33
  if (this.vtxoSubscription) {
@@ -366,7 +366,6 @@ export class Wallet {
366
366
  amount: BigInt(input.value),
367
367
  },
368
368
  tapInternalKey: this.onchainP2TR.tapInternalKey,
369
- tapMerkleRoot: this.onchainP2TR.tapMerkleRoot,
370
369
  });
371
370
  }
372
371
  // Add payment output
@@ -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>;
@@ -6,7 +6,7 @@ export declare class Worker {
6
6
  private arkProvider;
7
7
  private vtxoSubscription;
8
8
  constructor(vtxoRepository?: VtxoRepository, messageCallback?: (message: ExtendableMessageEvent) => void);
9
- start(): Promise<void>;
9
+ start(withServiceWorkerUpdate?: boolean): Promise<void>;
10
10
  clear(): Promise<void>;
11
11
  private onWalletInitialized;
12
12
  private processVtxoSubscription;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arkade-os/sdk",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Bitcoin wallet SDK with Taproot and Ark integration",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",
@@ -23,11 +23,11 @@
23
23
  "registry": "https://registry.npmjs.org/"
24
24
  },
25
25
  "dependencies": {
26
- "@noble/curves": "1.7.0",
27
- "@noble/hashes": "1.6.1",
26
+ "@noble/curves": "1.9.1",
27
+ "@noble/hashes": "1.8.0",
28
28
  "@noble/secp256k1": "2.2.3",
29
- "@scure/base": "1.2.1",
30
- "@scure/btc-signer": "1.7.0",
29
+ "@scure/base": "1.2.6",
30
+ "@scure/btc-signer": "1.8.1",
31
31
  "bip68": "1.0.4"
32
32
  },
33
33
  "devDependencies": {
@@ -36,7 +36,7 @@
36
36
  "@types/node": "22.10.2",
37
37
  "@typescript-eslint/eslint-plugin": "8.18.2",
38
38
  "@typescript-eslint/parser": "8.18.2",
39
- "@vitest/coverage-v8": "2.1.8",
39
+ "@vitest/coverage-v8": "2.1.9",
40
40
  "esbuild": "^0.20.1",
41
41
  "eslint": "^9.17.0",
42
42
  "glob": "11.0.1",
@@ -44,7 +44,7 @@
44
44
  "lint-staged": "15.3.0",
45
45
  "prettier": "3.4.2",
46
46
  "typescript": "5.7.2",
47
- "vitest": "2.1.8"
47
+ "vitest": "2.1.9"
48
48
  },
49
49
  "keywords": [
50
50
  "bitcoin",