@arkade-os/sdk 0.2.2 → 0.3.0-alpha.0

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.
Files changed (81) hide show
  1. package/README.md +114 -43
  2. package/dist/cjs/adapters/asyncStorage.js +5 -0
  3. package/dist/cjs/adapters/fileSystem.js +5 -0
  4. package/dist/cjs/adapters/indexedDB.js +5 -0
  5. package/dist/cjs/adapters/localStorage.js +5 -0
  6. package/dist/cjs/bip322/index.js +2 -2
  7. package/dist/cjs/identity/index.js +15 -0
  8. package/dist/cjs/identity/singleKey.js +20 -1
  9. package/dist/cjs/index.js +5 -3
  10. package/dist/cjs/musig2/keys.js +6 -6
  11. package/dist/cjs/musig2/sign.js +5 -5
  12. package/dist/cjs/repositories/contractRepository.js +130 -0
  13. package/dist/cjs/repositories/index.js +18 -0
  14. package/dist/cjs/repositories/walletRepository.js +136 -0
  15. package/dist/cjs/storage/asyncStorage.js +47 -0
  16. package/dist/cjs/storage/fileSystem.js +138 -0
  17. package/dist/cjs/storage/inMemory.js +21 -0
  18. package/dist/cjs/storage/indexedDB.js +97 -0
  19. package/dist/cjs/storage/localStorage.js +48 -0
  20. package/dist/cjs/tree/signingSession.js +4 -4
  21. package/dist/cjs/wallet/onchain.js +12 -6
  22. package/dist/cjs/wallet/serviceWorker/request.js +4 -14
  23. package/dist/cjs/wallet/serviceWorker/response.js +0 -13
  24. package/dist/cjs/wallet/serviceWorker/wallet.js +124 -130
  25. package/dist/cjs/wallet/serviceWorker/worker.js +89 -53
  26. package/dist/cjs/wallet/wallet.js +21 -4
  27. package/dist/esm/adapters/asyncStorage.js +1 -0
  28. package/dist/esm/adapters/fileSystem.js +1 -0
  29. package/dist/esm/adapters/indexedDB.js +1 -0
  30. package/dist/esm/adapters/localStorage.js +1 -0
  31. package/dist/esm/bip322/index.js +1 -1
  32. package/dist/esm/identity/index.js +1 -1
  33. package/dist/esm/identity/singleKey.js +21 -2
  34. package/dist/esm/index.js +4 -3
  35. package/dist/esm/musig2/keys.js +6 -6
  36. package/dist/esm/musig2/sign.js +4 -4
  37. package/dist/esm/repositories/contractRepository.js +126 -0
  38. package/dist/esm/repositories/index.js +2 -0
  39. package/dist/esm/repositories/walletRepository.js +132 -0
  40. package/dist/esm/storage/asyncStorage.js +43 -0
  41. package/dist/esm/storage/fileSystem.js +101 -0
  42. package/dist/esm/storage/inMemory.js +17 -0
  43. package/dist/esm/storage/indexedDB.js +93 -0
  44. package/dist/esm/storage/localStorage.js +44 -0
  45. package/dist/esm/tree/signingSession.js +1 -1
  46. package/dist/esm/wallet/onchain.js +12 -6
  47. package/dist/esm/wallet/serviceWorker/request.js +4 -14
  48. package/dist/esm/wallet/serviceWorker/response.js +0 -13
  49. package/dist/esm/wallet/serviceWorker/wallet.js +125 -131
  50. package/dist/esm/wallet/serviceWorker/worker.js +90 -54
  51. package/dist/esm/wallet/wallet.js +21 -4
  52. package/dist/types/adapters/asyncStorage.d.ts +2 -0
  53. package/dist/types/adapters/fileSystem.d.ts +2 -0
  54. package/dist/types/adapters/indexedDB.d.ts +2 -0
  55. package/dist/types/adapters/localStorage.d.ts +2 -0
  56. package/dist/types/identity/index.d.ts +3 -1
  57. package/dist/types/identity/singleKey.d.ts +12 -1
  58. package/dist/types/index.d.ts +4 -4
  59. package/dist/types/repositories/contractRepository.d.ts +20 -0
  60. package/dist/types/repositories/index.d.ts +2 -0
  61. package/dist/types/repositories/walletRepository.d.ts +38 -0
  62. package/dist/types/storage/asyncStorage.d.ts +9 -0
  63. package/dist/types/storage/fileSystem.d.ts +11 -0
  64. package/dist/types/storage/inMemory.d.ts +8 -0
  65. package/dist/types/storage/index.d.ts +6 -0
  66. package/dist/types/storage/indexedDB.d.ts +12 -0
  67. package/dist/types/storage/localStorage.d.ts +8 -0
  68. package/dist/types/wallet/index.d.ts +3 -0
  69. package/dist/types/wallet/onchain.d.ts +3 -2
  70. package/dist/types/wallet/serviceWorker/request.d.ts +1 -7
  71. package/dist/types/wallet/serviceWorker/response.d.ts +1 -8
  72. package/dist/types/wallet/serviceWorker/wallet.d.ts +67 -21
  73. package/dist/types/wallet/serviceWorker/worker.d.ts +17 -4
  74. package/dist/types/wallet/wallet.d.ts +4 -0
  75. package/package.json +38 -14
  76. package/dist/cjs/wallet/serviceWorker/db/vtxo/idb.js +0 -185
  77. package/dist/esm/wallet/serviceWorker/db/vtxo/idb.js +0 -181
  78. package/dist/types/wallet/serviceWorker/db/vtxo/idb.d.ts +0 -20
  79. package/dist/types/wallet/serviceWorker/db/vtxo/index.d.ts +0 -14
  80. /package/dist/cjs/{wallet/serviceWorker/db/vtxo → storage}/index.js +0 -0
  81. /package/dist/esm/{wallet/serviceWorker/db/vtxo → storage}/index.js +0 -0
@@ -3,93 +3,91 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ServiceWorkerWallet = void 0;
4
4
  const response_1 = require("./response");
5
5
  const base_1 = require("@scure/base");
6
- const singleKey_1 = require("../../identity/singleKey");
7
- const signingSession_1 = require("../../tree/signingSession");
8
- const btc_signer_1 = require("@scure/btc-signer");
6
+ const indexedDB_1 = require("../../storage/indexedDB");
7
+ const walletRepository_1 = require("../../repositories/walletRepository");
8
+ const contractRepository_1 = require("../../repositories/contractRepository");
9
+ const utils_1 = require("./utils");
10
+ const isPrivateKeyIdentity = (identity) => {
11
+ return (identity.toHex !== undefined &&
12
+ typeof identity.toHex === "function" &&
13
+ typeof identity.toHex() === "string" &&
14
+ identity.toHex().length > 0);
15
+ };
9
16
  class UnexpectedResponseError extends Error {
10
17
  constructor(response) {
11
18
  super(`Unexpected response type. Got: ${JSON.stringify(response, null, 2)}`);
12
19
  this.name = "UnexpectedResponseError";
13
20
  }
14
21
  }
15
- /**
16
- * Service Worker-based wallet implementation for browser environments.
17
- *
18
- * This wallet uses a service worker as a backend to handle wallet logic,
19
- * providing secure key storage and transaction signing in web applications.
20
- * The service worker runs in a separate thread and can persist data between
21
- * browser sessions.
22
- *
23
- * @example
24
- * ```typescript
25
- * // Create and initialize the service worker wallet
26
- * const serviceWorker = await setupServiceWorker("/service-worker.js");
27
- * const wallet = new ServiceWorkerWallet(serviceWorker);
28
- * await wallet.init({
29
- * privateKey: 'your_private_key_hex',
30
- * arkServerUrl: 'https://ark.example.com'
31
- * });
32
- *
33
- * // Use like any other wallet
34
- * const address = await wallet.getAddress();
35
- * const balance = await wallet.getBalance();
36
- * ```
37
- */
38
22
  class ServiceWorkerWallet {
39
- constructor(serviceWorker) {
23
+ constructor(serviceWorker, identity, walletRepository, contractRepository) {
40
24
  this.serviceWorker = serviceWorker;
25
+ this.identity = identity;
26
+ this.walletRepository = walletRepository;
27
+ this.contractRepository = contractRepository;
41
28
  }
42
- async getStatus() {
43
- const message = {
44
- type: "GET_STATUS",
45
- id: getRandomId(),
46
- };
47
- const response = await this.sendMessage(message);
48
- if (response_1.Response.isWalletStatus(response)) {
49
- const { walletInitialized, xOnlyPublicKey } = response.status;
50
- if (walletInitialized)
51
- this.cachedXOnlyPublicKey = xOnlyPublicKey;
52
- return response.status;
53
- }
54
- throw new UnexpectedResponseError(response);
55
- }
56
- async init(config, failIfInitialized = false) {
57
- // Check if wallet is already initialized
58
- const statusMessage = {
59
- type: "GET_STATUS",
60
- id: getRandomId(),
61
- };
62
- const response = await this.sendMessage(statusMessage);
63
- if (response_1.Response.isWalletStatus(response) &&
64
- response.status.walletInitialized) {
65
- if (failIfInitialized) {
66
- throw new Error("Wallet already initialized");
67
- }
68
- this.cachedXOnlyPublicKey = response.status.xOnlyPublicKey;
69
- return;
29
+ static async create(options) {
30
+ // Default to IndexedDB for service worker context
31
+ const storage = options.storage || new indexedDB_1.IndexedDBStorageAdapter("wallet-db");
32
+ // Create repositories
33
+ const walletRepo = new walletRepository_1.WalletRepositoryImpl(storage);
34
+ const contractRepo = new contractRepository_1.ContractRepositoryImpl(storage);
35
+ // Extract identity and check if it can expose private key
36
+ const identity = isPrivateKeyIdentity(options.identity)
37
+ ? options.identity
38
+ : null;
39
+ if (!identity) {
40
+ throw new Error("ServiceWorkerWallet.create() requires a Identity that can expose its private key");
70
41
  }
71
- // If not initialized, proceed with initialization
72
- const message = {
42
+ // Extract private key for service worker initialization
43
+ const privateKey = identity.toHex();
44
+ // Create the wallet instance
45
+ const wallet = new ServiceWorkerWallet(options.serviceWorker, identity, walletRepo, contractRepo);
46
+ // Initialize the service worker with the config
47
+ const initMessage = {
73
48
  type: "INIT_WALLET",
74
49
  id: getRandomId(),
75
- privateKey: config.privateKey,
76
- arkServerUrl: config.arkServerUrl,
77
- arkServerPublicKey: config.arkServerPublicKey,
50
+ privateKey,
51
+ arkServerUrl: options.arkServerUrl,
52
+ arkServerPublicKey: options.arkServerPublicKey,
78
53
  };
79
- await this.sendMessage(message);
80
- const privKeyBytes = base_1.hex.decode(config.privateKey);
81
- // cache the identity xOnlyPublicKey
82
- this.cachedXOnlyPublicKey =
83
- singleKey_1.SingleKey.fromPrivateKey(privKeyBytes).xOnlyPublicKey();
54
+ // Initialize the service worker
55
+ await wallet.sendMessage(initMessage);
56
+ return wallet;
84
57
  }
85
- async clear() {
86
- const message = {
87
- type: "CLEAR",
88
- id: getRandomId(),
89
- };
90
- await this.sendMessage(message);
91
- // clear the cached xOnlyPublicKey
92
- this.cachedXOnlyPublicKey = undefined;
58
+ /**
59
+ * Simplified setup method that handles service worker registration,
60
+ * identity creation, and wallet initialization automatically.
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * // One-liner setup - handles everything automatically!
65
+ * const wallet = await ServiceWorkerWallet.setup({
66
+ * serviceWorkerPath: '/service-worker.js',
67
+ * arkServerUrl: 'https://mutinynet.arkade.sh'
68
+ * });
69
+ *
70
+ * // With custom identity
71
+ * const identity = SingleKey.fromHex('your_private_key_hex');
72
+ * const wallet = await ServiceWorkerWallet.setup({
73
+ * serviceWorkerPath: '/service-worker.js',
74
+ * arkServerUrl: 'https://mutinynet.arkade.sh',
75
+ * identity
76
+ * });
77
+ * ```
78
+ */
79
+ static async setup(options) {
80
+ // Register and setup the service worker
81
+ const serviceWorker = await (0, utils_1.setupServiceWorker)(options.serviceWorkerPath);
82
+ // Use the existing create method
83
+ return await ServiceWorkerWallet.create({
84
+ arkServerPublicKey: options.arkServerPublicKey,
85
+ arkServerUrl: options.arkServerUrl,
86
+ esploraUrl: options.esploraUrl,
87
+ identity: options.identity,
88
+ serviceWorker,
89
+ storage: options.storage,
90
+ });
93
91
  }
94
92
  // send a message and wait for a response
95
93
  async sendMessage(message) {
@@ -115,6 +113,21 @@ class ServiceWorkerWallet {
115
113
  this.serviceWorker.postMessage(message);
116
114
  });
117
115
  }
116
+ async clear() {
117
+ const message = {
118
+ type: "CLEAR",
119
+ id: getRandomId(),
120
+ };
121
+ // Clear page-side storage to maintain parity with SW
122
+ try {
123
+ const address = await this.getAddress();
124
+ await this.walletRepository.clearVtxos(address);
125
+ }
126
+ catch (_) {
127
+ console.warn("Failed to clear vtxos from wallet repository");
128
+ }
129
+ await this.sendMessage(message);
130
+ }
118
131
  async getAddress() {
119
132
  const message = {
120
133
  type: "GET_ADDRESS",
@@ -163,37 +176,64 @@ class ServiceWorkerWallet {
163
176
  throw new Error(`Failed to get balance: ${error}`);
164
177
  }
165
178
  }
166
- async getVtxos(filter) {
179
+ async getBoardingUtxos() {
167
180
  const message = {
168
- type: "GET_VTXOS",
181
+ type: "GET_BOARDING_UTXOS",
169
182
  id: getRandomId(),
170
- filter,
171
183
  };
172
184
  try {
173
185
  const response = await this.sendMessage(message);
174
- if (response_1.Response.isVtxos(response)) {
175
- return response.vtxos;
186
+ if (response_1.Response.isBoardingUtxos(response)) {
187
+ return response.boardingUtxos;
176
188
  }
177
189
  throw new UnexpectedResponseError(response);
178
190
  }
179
191
  catch (error) {
180
- throw new Error(`Failed to get vtxos: ${error}`);
192
+ throw new Error(`Failed to get boarding UTXOs: ${error}`);
181
193
  }
182
194
  }
183
- async getBoardingUtxos() {
195
+ async getStatus() {
184
196
  const message = {
185
- type: "GET_BOARDING_UTXOS",
197
+ type: "GET_STATUS",
198
+ id: getRandomId(),
199
+ };
200
+ const response = await this.sendMessage(message);
201
+ if (response_1.Response.isWalletStatus(response)) {
202
+ return response.status;
203
+ }
204
+ throw new UnexpectedResponseError(response);
205
+ }
206
+ async getTransactionHistory() {
207
+ const message = {
208
+ type: "GET_TRANSACTION_HISTORY",
186
209
  id: getRandomId(),
187
210
  };
188
211
  try {
189
212
  const response = await this.sendMessage(message);
190
- if (response_1.Response.isBoardingUtxos(response)) {
191
- return response.boardingUtxos;
213
+ if (response_1.Response.isTransactionHistory(response)) {
214
+ return response.transactions;
192
215
  }
193
216
  throw new UnexpectedResponseError(response);
194
217
  }
195
218
  catch (error) {
196
- throw new Error(`Failed to get boarding UTXOs: ${error}`);
219
+ throw new Error(`Failed to get transaction history: ${error}`);
220
+ }
221
+ }
222
+ async getVtxos(filter) {
223
+ const message = {
224
+ type: "GET_VTXOS",
225
+ id: getRandomId(),
226
+ filter,
227
+ };
228
+ try {
229
+ const response = await this.sendMessage(message);
230
+ if (response_1.Response.isVtxos(response)) {
231
+ return response.vtxos;
232
+ }
233
+ throw new UnexpectedResponseError(response);
234
+ }
235
+ catch (error) {
236
+ throw new Error(`Failed to get vtxos: ${error}`);
197
237
  }
198
238
  }
199
239
  async sendBitcoin(params) {
@@ -250,52 +290,6 @@ class ServiceWorkerWallet {
250
290
  throw new Error(`Settlement failed: ${error}`);
251
291
  }
252
292
  }
253
- async getTransactionHistory() {
254
- const message = {
255
- type: "GET_TRANSACTION_HISTORY",
256
- id: getRandomId(),
257
- };
258
- try {
259
- const response = await this.sendMessage(message);
260
- if (response_1.Response.isTransactionHistory(response)) {
261
- return response.transactions;
262
- }
263
- throw new UnexpectedResponseError(response);
264
- }
265
- catch (error) {
266
- throw new Error(`Failed to get transaction history: ${error}`);
267
- }
268
- }
269
- xOnlyPublicKey() {
270
- if (!this.cachedXOnlyPublicKey) {
271
- throw new Error("Wallet not initialized");
272
- }
273
- return this.cachedXOnlyPublicKey;
274
- }
275
- signerSession() {
276
- return signingSession_1.TreeSignerSession.random();
277
- }
278
- async sign(tx, inputIndexes) {
279
- const message = {
280
- type: "SIGN",
281
- tx: base_1.base64.encode(tx.toPSBT()),
282
- inputIndexes,
283
- id: getRandomId(),
284
- };
285
- try {
286
- const response = await this.sendMessage(message);
287
- if (response_1.Response.isSignSuccess(response)) {
288
- return btc_signer_1.Transaction.fromPSBT(base_1.base64.decode(response.tx), {
289
- allowUnknown: true,
290
- allowUnknownInputs: true,
291
- });
292
- }
293
- throw new UnexpectedResponseError(response);
294
- }
295
- catch (error) {
296
- throw new Error(`Failed to sign: ${error}`);
297
- }
298
- }
299
293
  }
300
294
  exports.ServiceWorkerWallet = ServiceWorkerWallet;
301
295
  function getRandomId() {
@@ -8,19 +8,53 @@ const wallet_1 = require("../wallet");
8
8
  const request_1 = require("./request");
9
9
  const response_1 = require("./response");
10
10
  const ark_1 = require("../../providers/ark");
11
- const idb_1 = require("./db/vtxo/idb");
12
11
  const transactionHistory_1 = require("../../utils/transactionHistory");
13
12
  const indexer_1 = require("../../providers/indexer");
14
13
  const base_1 = require("@scure/base");
15
- const btc_signer_1 = require("@scure/btc-signer");
14
+ const indexedDB_1 = require("../../storage/indexedDB");
15
+ const walletRepository_1 = require("../../repositories/walletRepository");
16
16
  /**
17
17
  * Worker is a class letting to interact with ServiceWorkerWallet from the client
18
18
  * it aims to be run in a service worker context
19
19
  */
20
20
  class Worker {
21
- constructor(vtxoRepository = new idb_1.IndexedDBVtxoRepository(), messageCallback = () => { }) {
22
- this.vtxoRepository = vtxoRepository;
21
+ constructor(messageCallback = () => { }) {
23
22
  this.messageCallback = messageCallback;
23
+ this.storage = new indexedDB_1.IndexedDBStorageAdapter("arkade-service-worker", 1);
24
+ this.walletRepository = new walletRepository_1.WalletRepositoryImpl(this.storage);
25
+ }
26
+ /**
27
+ * Get spendable vtxos for the current wallet address
28
+ */
29
+ async getSpendableVtxos() {
30
+ if (!this.wallet)
31
+ return [];
32
+ const address = await this.wallet.getAddress();
33
+ const allVtxos = await this.walletRepository.getVtxos(address);
34
+ return allVtxos.filter(__1.isSpendable);
35
+ }
36
+ /**
37
+ * Get swept vtxos for the current wallet address
38
+ */
39
+ async getSweptVtxos() {
40
+ if (!this.wallet)
41
+ return [];
42
+ const address = await this.wallet.getAddress();
43
+ const allVtxos = await this.walletRepository.getVtxos(address);
44
+ return allVtxos.filter((vtxo) => vtxo.virtualStatus.state === "swept" && (0, __1.isSpendable)(vtxo));
45
+ }
46
+ /**
47
+ * Get all vtxos categorized by type
48
+ */
49
+ async getAllVtxos() {
50
+ if (!this.wallet)
51
+ return { spendable: [], spent: [] };
52
+ const address = await this.wallet.getAddress();
53
+ const allVtxos = await this.walletRepository.getVtxos(address);
54
+ return {
55
+ spendable: allVtxos.filter(__1.isSpendable),
56
+ spent: allVtxos.filter((vtxo) => !(0, __1.isSpendable)(vtxo)),
57
+ };
24
58
  }
25
59
  async start(withServiceWorkerUpdate = true) {
26
60
  self.addEventListener("message", async (event) => {
@@ -41,12 +75,18 @@ class Worker {
41
75
  if (this.vtxoSubscription) {
42
76
  this.vtxoSubscription.abort();
43
77
  }
44
- await this.vtxoRepository.close();
78
+ // Clear storage - this replaces vtxoRepository.close()
79
+ await this.storage.clear();
45
80
  this.wallet = undefined;
46
81
  this.arkProvider = undefined;
47
82
  this.indexerProvider = undefined;
48
83
  this.vtxoSubscription = undefined;
49
84
  }
85
+ async reload() {
86
+ if (this.vtxoSubscription)
87
+ this.vtxoSubscription.abort();
88
+ await this.onWalletInitialized();
89
+ }
50
90
  async onWalletInitialized() {
51
91
  if (!this.wallet ||
52
92
  !this.arkProvider ||
@@ -55,8 +95,6 @@ class Worker {
55
95
  !this.wallet.boardingTapscript) {
56
96
  return;
57
97
  }
58
- // subscribe to address updates
59
- await this.vtxoRepository.open();
60
98
  const encodedOffchainTapscript = this.wallet.offchainTapscript.encode();
61
99
  const forfeit = this.wallet.offchainTapscript.forfeit();
62
100
  const exit = this.wallet.offchainTapscript.exit();
@@ -71,7 +109,9 @@ class Worker {
71
109
  intentTapLeafScript: exit,
72
110
  tapTree: encodedOffchainTapscript,
73
111
  }));
74
- await this.vtxoRepository.addOrUpdate(vtxos);
112
+ // Get wallet address and save vtxos using unified repository
113
+ const address = await this.wallet.getAddress();
114
+ await this.walletRepository.saveVtxos(address, vtxos);
75
115
  this.processVtxoSubscription({
76
116
  script,
77
117
  vtxoScript: this.wallet.offchainTapscript,
@@ -97,7 +137,11 @@ class Worker {
97
137
  intentTapLeafScript,
98
138
  tapTree,
99
139
  }));
100
- await this.vtxoRepository.addOrUpdate(extendedVtxos);
140
+ // Get wallet address and save vtxos using unified repository
141
+ const address = await this.wallet.getAddress();
142
+ await this.walletRepository.saveVtxos(address, extendedVtxos);
143
+ // Notify all clients about the vtxo update
144
+ this.sendMessageToAllClients("VTXO_UPDATE", "");
101
145
  }
102
146
  }
103
147
  catch (error) {
@@ -105,7 +149,7 @@ class Worker {
105
149
  }
106
150
  }
107
151
  async handleClear(event) {
108
- this.clear();
152
+ await this.clear();
109
153
  if (request_1.Request.isBase(event.data)) {
110
154
  event.source?.postMessage(response_1.Response.clearResponse(event.data.id, true));
111
155
  }
@@ -117,13 +161,22 @@ class Worker {
117
161
  event.source?.postMessage(response_1.Response.error(message.id, "Invalid INIT_WALLET message format"));
118
162
  return;
119
163
  }
164
+ if (!message.privateKey) {
165
+ const err = "Missing privateKey";
166
+ event.source?.postMessage(response_1.Response.error(message.id, err));
167
+ console.error(err);
168
+ return;
169
+ }
120
170
  try {
121
- this.arkProvider = new ark_1.RestArkProvider(message.arkServerUrl);
122
- this.indexerProvider = new indexer_1.RestIndexerProvider(message.arkServerUrl);
171
+ const { arkServerPublicKey, arkServerUrl, privateKey } = message;
172
+ const identity = singleKey_1.SingleKey.fromHex(privateKey);
173
+ this.arkProvider = new ark_1.RestArkProvider(arkServerUrl);
174
+ this.indexerProvider = new indexer_1.RestIndexerProvider(arkServerUrl);
123
175
  this.wallet = await wallet_1.Wallet.create({
124
- identity: singleKey_1.SingleKey.fromHex(message.privateKey),
125
- arkServerUrl: message.arkServerUrl,
126
- arkServerPublicKey: message.arkServerPublicKey,
176
+ identity,
177
+ arkServerUrl,
178
+ arkServerPublicKey,
179
+ storage: this.storage, // Use unified storage for wallet too
127
180
  });
128
181
  event.source?.postMessage(response_1.Response.walletInitialized(message.id));
129
182
  await this.onWalletInitialized();
@@ -249,8 +302,8 @@ class Worker {
249
302
  try {
250
303
  const [boardingUtxos, spendableVtxos, sweptVtxos] = await Promise.all([
251
304
  this.wallet.getBoardingUtxos(),
252
- this.vtxoRepository.getSpendableVtxos(),
253
- this.vtxoRepository.getSweptVtxos(),
305
+ this.getSpendableVtxos(),
306
+ this.getSweptVtxos(),
254
307
  ]);
255
308
  // boarding
256
309
  let confirmed = 0;
@@ -316,7 +369,7 @@ class Worker {
316
369
  return;
317
370
  }
318
371
  try {
319
- let vtxos = await this.vtxoRepository.getSpendableVtxos();
372
+ let vtxos = await this.getSpendableVtxos();
320
373
  if (!message.filter?.withRecoverable) {
321
374
  if (!this.wallet)
322
375
  throw new Error("Wallet not initialized");
@@ -325,7 +378,7 @@ class Worker {
325
378
  }
326
379
  if (message.filter?.withRecoverable) {
327
380
  // get also swept and spendable vtxos
328
- const sweptVtxos = await this.vtxoRepository.getSweptVtxos();
381
+ const sweptVtxos = await this.getSweptVtxos();
329
382
  vtxos.push(...sweptVtxos.filter(__1.isSpendable));
330
383
  }
331
384
  event.source?.postMessage(response_1.Response.vtxos(message.id, vtxos));
@@ -376,7 +429,7 @@ class Worker {
376
429
  }
377
430
  try {
378
431
  const { boardingTxs, commitmentsToIgnore: roundsToIgnore } = await this.wallet.getBoardingTxs();
379
- const { spendable, spent } = await this.vtxoRepository.getAllVtxos();
432
+ const { spendable, spent } = await this.getAllVtxos();
380
433
  // convert VTXOs to offchain transactions
381
434
  const offchainTxs = (0, transactionHistory_1.vtxosToTxs)(spendable, spent, roundsToIgnore);
382
435
  const txs = [...boardingTxs, ...offchainTxs];
@@ -407,35 +460,10 @@ class Worker {
407
460
  event.source?.postMessage(response_1.Response.error(message.id, "Invalid GET_STATUS message format"));
408
461
  return;
409
462
  }
410
- event.source?.postMessage(response_1.Response.walletStatus(message.id, this.wallet !== undefined, this.wallet?.identity.xOnlyPublicKey()));
411
- }
412
- async handleSign(event) {
413
- const message = event.data;
414
- if (!request_1.Request.isSign(message)) {
415
- console.error("Invalid SIGN message format", message);
416
- event.source?.postMessage(response_1.Response.error(message.id, "Invalid SIGN message format"));
417
- return;
418
- }
419
- if (!this.wallet) {
420
- console.error("Wallet not initialized");
421
- event.source?.postMessage(response_1.Response.error(message.id, "Wallet not initialized"));
422
- return;
423
- }
424
- try {
425
- const tx = btc_signer_1.Transaction.fromPSBT(base_1.base64.decode(message.tx), {
426
- allowUnknown: true,
427
- allowUnknownInputs: true,
428
- });
429
- const signedTx = await this.wallet.identity.sign(tx, message.inputIndexes);
430
- event.source?.postMessage(response_1.Response.signSuccess(message.id, base_1.base64.encode(signedTx.toPSBT())));
431
- }
432
- catch (error) {
433
- console.error("Error signing:", error);
434
- const errorMessage = error instanceof Error
435
- ? error.message
436
- : "Unknown error occurred";
437
- event.source?.postMessage(response_1.Response.error(message.id, errorMessage));
438
- }
463
+ const pubKey = this.wallet
464
+ ? await this.wallet.identity.xOnlyPublicKey()
465
+ : undefined;
466
+ event.source?.postMessage(response_1.Response.walletStatus(message.id, this.wallet !== undefined, pubKey));
439
467
  }
440
468
  async handleMessage(event) {
441
469
  this.messageCallback(event);
@@ -490,13 +518,21 @@ class Worker {
490
518
  await this.handleClear(event);
491
519
  break;
492
520
  }
493
- case "SIGN": {
494
- await this.handleSign(event);
495
- break;
496
- }
497
521
  default:
498
522
  event.source?.postMessage(response_1.Response.error(message.id, "Unknown message type"));
499
523
  }
500
524
  }
525
+ async sendMessageToAllClients(type, message) {
526
+ self.clients
527
+ .matchAll({ includeUncontrolled: true, type: "window" })
528
+ .then((clients) => {
529
+ clients.forEach((client) => {
530
+ client.postMessage({
531
+ type,
532
+ message,
533
+ });
534
+ });
535
+ });
536
+ }
501
537
  }
502
538
  exports.Worker = Worker;
@@ -57,6 +57,9 @@ const arknote_1 = require("../arknote");
57
57
  const bip322_1 = require("../bip322");
58
58
  const indexer_1 = require("../providers/indexer");
59
59
  const txTree_1 = require("../tree/txTree");
60
+ const inMemory_1 = require("../storage/inMemory");
61
+ const walletRepository_1 = require("../repositories/walletRepository");
62
+ const contractRepository_1 = require("../repositories/contractRepository");
60
63
  /**
61
64
  * Main wallet implementation for Bitcoin transactions with Ark protocol support.
62
65
  * The wallet does not store any data locally and relies on Ark and onchain
@@ -83,7 +86,7 @@ const txTree_1 = require("../tree/txTree");
83
86
  * ```
84
87
  */
85
88
  class Wallet {
86
- constructor(identity, network, networkName, onchainProvider, arkProvider, indexerProvider, arkServerPublicKey, offchainTapscript, boardingTapscript, serverUnrollScript, forfeitOutputScript, dustAmount) {
89
+ constructor(identity, network, networkName, onchainProvider, arkProvider, indexerProvider, arkServerPublicKey, offchainTapscript, boardingTapscript, serverUnrollScript, forfeitOutputScript, dustAmount, walletRepository, contractRepository) {
87
90
  this.identity = identity;
88
91
  this.network = network;
89
92
  this.networkName = networkName;
@@ -96,9 +99,11 @@ class Wallet {
96
99
  this.serverUnrollScript = serverUnrollScript;
97
100
  this.forfeitOutputScript = forfeitOutputScript;
98
101
  this.dustAmount = dustAmount;
102
+ this.walletRepository = walletRepository;
103
+ this.contractRepository = contractRepository;
99
104
  }
100
105
  static async create(config) {
101
- const pubkey = config.identity.xOnlyPublicKey();
106
+ const pubkey = await config.identity.xOnlyPublicKey();
102
107
  if (!pubkey) {
103
108
  throw new Error("Invalid configured public key");
104
109
  }
@@ -138,7 +143,11 @@ class Wallet {
138
143
  // server is expecting funds to be sent to this address
139
144
  const forfeitAddress = (0, payment_1.Address)(network).decode(info.forfeitAddress);
140
145
  const forfeitOutputScript = payment_1.OutScript.encode(forfeitAddress);
141
- return new Wallet(config.identity, network, info.network, onchainProvider, arkProvider, indexerProvider, serverPubKey, offchainTapscript, boardingTapscript, serverUnrollScript, forfeitOutputScript, info.dust);
146
+ // Set up storage and repositories
147
+ const storage = config.storage || new inMemory_1.InMemoryStorageAdapter();
148
+ const walletRepository = new walletRepository_1.WalletRepositoryImpl(storage);
149
+ const contractRepository = new contractRepository_1.ContractRepositoryImpl(storage);
150
+ return new Wallet(config.identity, network, info.network, onchainProvider, arkProvider, indexerProvider, serverPubKey, offchainTapscript, boardingTapscript, serverUnrollScript, forfeitOutputScript, info.dust, walletRepository, contractRepository);
142
151
  }
143
152
  get arkAddress() {
144
153
  return this.offchainTapscript.address(this.network.hrp, this.arkServerPublicKey);
@@ -194,16 +203,24 @@ class Wallet {
194
203
  };
195
204
  }
196
205
  async getVtxos(filter) {
206
+ const address = await this.getAddress();
207
+ // Try to get from cache first
208
+ const cachedVtxos = await this.walletRepository.getVtxos(address);
209
+ // For now, always fetch fresh data from provider and update cache
210
+ // In future, we can add cache invalidation logic based on timestamps
197
211
  const spendableVtxos = await this.getVirtualCoins(filter);
198
212
  const encodedOffchainTapscript = this.offchainTapscript.encode();
199
213
  const forfeit = this.offchainTapscript.forfeit();
200
214
  const exit = this.offchainTapscript.exit();
201
- return spendableVtxos.map((vtxo) => ({
215
+ const extendedVtxos = spendableVtxos.map((vtxo) => ({
202
216
  ...vtxo,
203
217
  forfeitTapLeafScript: forfeit,
204
218
  intentTapLeafScript: exit,
205
219
  tapTree: encodedOffchainTapscript,
206
220
  }));
221
+ // Update cache with fresh data
222
+ await this.walletRepository.saveVtxos(address, extendedVtxos);
223
+ return extendedVtxos;
207
224
  }
208
225
  async getVirtualCoins(filter = { withRecoverable: true, withUnrolled: false }) {
209
226
  const scripts = [base_1.hex.encode(this.offchainTapscript.pkScript)];
@@ -0,0 +1 @@
1
+ export { AsyncStorageAdapter } from '../storage/asyncStorage.js';
@@ -0,0 +1 @@
1
+ export { FileSystemStorageAdapter } from '../storage/fileSystem.js';
@@ -0,0 +1 @@
1
+ export { IndexedDBStorageAdapter } from '../storage/indexedDB.js';
@@ -0,0 +1 @@
1
+ export { LocalStorageAdapter } from '../storage/localStorage.js';
@@ -1,6 +1,6 @@
1
1
  import { OP, Transaction, Script, SigHash } from "@scure/btc-signer";
2
2
  import { ErrMissingData, ErrMissingInputs, ErrMissingWitnessUtxo, } from './errors.js';
3
- import { schnorr } from "@noble/curves/secp256k1";
3
+ import { schnorr } from "@noble/curves/secp256k1.js";
4
4
  import { base64 } from "@scure/base";
5
5
  /**
6
6
  * BIP-322 signature implementation for Bitcoin message signing.
@@ -1 +1 @@
1
- export {};
1
+ export * from './singleKey.js';