@arkade-os/sdk 0.3.0-alpha.4 → 0.3.0-alpha.6

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 (28) hide show
  1. package/dist/cjs/providers/onchain.js +8 -3
  2. package/dist/cjs/repositories/contractRepository.js +4 -0
  3. package/dist/cjs/repositories/walletRepository.js +27 -10
  4. package/dist/cjs/wallet/serviceWorker/request.js +8 -0
  5. package/dist/cjs/wallet/serviceWorker/response.js +12 -0
  6. package/dist/cjs/wallet/serviceWorker/utils.js +9 -0
  7. package/dist/cjs/wallet/serviceWorker/wallet.js +12 -4
  8. package/dist/cjs/wallet/serviceWorker/worker.js +67 -52
  9. package/dist/cjs/wallet/wallet.js +3 -1
  10. package/dist/esm/providers/onchain.js +8 -3
  11. package/dist/esm/repositories/contractRepository.js +4 -0
  12. package/dist/esm/repositories/walletRepository.js +27 -10
  13. package/dist/esm/wallet/serviceWorker/request.js +8 -0
  14. package/dist/esm/wallet/serviceWorker/response.js +12 -0
  15. package/dist/esm/wallet/serviceWorker/utils.js +8 -0
  16. package/dist/esm/wallet/serviceWorker/wallet.js +12 -4
  17. package/dist/esm/wallet/serviceWorker/worker.js +67 -52
  18. package/dist/esm/wallet/wallet.js +3 -1
  19. package/dist/types/providers/onchain.d.ts +1 -0
  20. package/dist/types/repositories/contractRepository.d.ts +2 -0
  21. package/dist/types/repositories/walletRepository.d.ts +9 -12
  22. package/dist/types/wallet/serviceWorker/request.d.ts +6 -1
  23. package/dist/types/wallet/serviceWorker/response.d.ts +6 -1
  24. package/dist/types/wallet/serviceWorker/utils.d.ts +2 -0
  25. package/dist/types/wallet/serviceWorker/wallet.d.ts +1 -0
  26. package/dist/types/wallet/serviceWorker/worker.d.ts +2 -2
  27. package/dist/types/wallet/wallet.d.ts +3 -2
  28. package/package.json +1 -1
@@ -23,6 +23,7 @@ exports.ESPLORA_URL = {
23
23
  class EsploraProvider {
24
24
  constructor(baseUrl) {
25
25
  this.baseUrl = baseUrl;
26
+ this.polling = false;
26
27
  }
27
28
  async getCoins(address) {
28
29
  const response = await fetch(`${this.baseUrl}/address/${address}/utxo`);
@@ -93,6 +94,9 @@ class EsploraProvider {
93
94
  let intervalId = null;
94
95
  const wsUrl = this.baseUrl.replace(/^http(s)?:/, "ws$1:") + "/v1/ws";
95
96
  const poll = async () => {
97
+ if (this.polling)
98
+ return;
99
+ this.polling = true;
96
100
  // websocket is not reliable, so we will fallback to polling
97
101
  const pollingInterval = 5000; // 5 seconds
98
102
  const getAllTxs = () => {
@@ -102,19 +106,19 @@ class EsploraProvider {
102
106
  const initialTxs = await getAllTxs();
103
107
  // we use block_time in key to also notify when a transaction is confirmed
104
108
  const txKey = (tx) => `${tx.txid}_${tx.status.block_time}`;
109
+ // create a set of existing transactions to avoid duplicates
110
+ const existingTxs = new Set(initialTxs.map(txKey));
105
111
  // polling for new transactions
106
112
  intervalId = setInterval(async () => {
107
113
  try {
108
114
  // get current transactions
109
115
  // we will compare with initialTxs to find new ones
110
116
  const currentTxs = await getAllTxs();
111
- // create a set of existing transactions to avoid duplicates
112
- const existingTxs = new Set(initialTxs.map(txKey));
113
117
  // filter out transactions that are already in initialTxs
114
118
  const newTxs = currentTxs.filter((tx) => !existingTxs.has(txKey(tx)));
115
119
  if (newTxs.length > 0) {
116
120
  // Update the tracking set instead of growing the array
117
- initialTxs.push(...newTxs);
121
+ newTxs.forEach((tx) => existingTxs.add(txKey(tx)));
118
122
  callback(newTxs);
119
123
  }
120
124
  }
@@ -175,6 +179,7 @@ class EsploraProvider {
175
179
  ws.close();
176
180
  if (intervalId)
177
181
  clearInterval(intervalId);
182
+ this.polling = false;
178
183
  };
179
184
  return stopFunc;
180
185
  }
@@ -126,5 +126,9 @@ class ContractRepositoryImpl {
126
126
  throw error; // Rethrow to notify caller of failure
127
127
  }
128
128
  }
129
+ async clearContractData() {
130
+ await this.storage.clear();
131
+ this.cache.clear();
132
+ }
129
133
  }
130
134
  exports.ContractRepositoryImpl = ContractRepositoryImpl;
@@ -7,8 +7,7 @@ const btc_signer_1 = require("@scure/btc-signer");
7
7
  const toHex = (b) => (b ? base_1.hex.encode(b) : undefined);
8
8
  const fromHex = (h) => h ? base_1.hex.decode(h) : undefined;
9
9
  const serializeTapLeaf = ([cb, s]) => ({
10
- cb: btc_signer_1.TaprootControlBlock.encode(cb) &&
11
- base_1.hex.encode(btc_signer_1.TaprootControlBlock.encode(cb)),
10
+ cb: base_1.hex.encode(btc_signer_1.TaprootControlBlock.encode(cb)),
12
11
  s: base_1.hex.encode(s),
13
12
  });
14
13
  const serializeVtxo = (v) => ({
@@ -53,7 +52,7 @@ class WalletRepositoryImpl {
53
52
  try {
54
53
  const parsed = JSON.parse(stored);
55
54
  const vtxos = parsed.map(deserializeVtxo);
56
- this.cache.vtxos.set(address, vtxos);
55
+ this.cache.vtxos.set(address, vtxos.slice());
57
56
  return vtxos.slice();
58
57
  }
59
58
  catch (error) {
@@ -71,7 +70,7 @@ class WalletRepositoryImpl {
71
70
  else {
72
71
  vtxos.push(vtxo);
73
72
  }
74
- this.cache.vtxos.set(address, vtxos);
73
+ this.cache.vtxos.set(address, vtxos.slice());
75
74
  await this.storage.setItem(`vtxos:${address}`, JSON.stringify(vtxos.map(serializeVtxo)));
76
75
  }
77
76
  async saveVtxos(address, vtxos) {
@@ -85,14 +84,14 @@ class WalletRepositoryImpl {
85
84
  storedVtxos.push(vtxo);
86
85
  }
87
86
  }
88
- this.cache.vtxos.set(address, storedVtxos);
87
+ this.cache.vtxos.set(address, storedVtxos.slice());
89
88
  await this.storage.setItem(`vtxos:${address}`, JSON.stringify(storedVtxos.map(serializeVtxo)));
90
89
  }
91
90
  async removeVtxo(address, vtxoId) {
92
91
  const vtxos = await this.getVtxos(address);
93
92
  const [txid, vout] = vtxoId.split(":");
94
- const filtered = vtxos.filter((v) => !(v.txid === txid && v.vout === parseInt(vout)));
95
- this.cache.vtxos.set(address, filtered);
93
+ const filtered = vtxos.filter((v) => !(v.txid === txid && v.vout === parseInt(vout, 10)));
94
+ this.cache.vtxos.set(address, filtered.slice());
96
95
  await this.storage.setItem(`vtxos:${address}`, JSON.stringify(filtered.map(serializeVtxo)));
97
96
  }
98
97
  async clearVtxos(address) {
@@ -122,18 +121,36 @@ class WalletRepositoryImpl {
122
121
  }
123
122
  async saveTransaction(address, tx) {
124
123
  const transactions = await this.getTransactionHistory(address);
125
- const existing = transactions.findIndex((t) => t.id === tx.id);
124
+ const existing = transactions.findIndex((t) => t.key === tx.key);
126
125
  if (existing !== -1) {
127
126
  transactions[existing] = tx;
128
127
  }
129
128
  else {
130
129
  transactions.push(tx);
131
- // Sort by timestamp descending
132
- transactions.sort((a, b) => b.timestamp - a.timestamp);
133
130
  }
131
+ // Sort by createdAt descending
132
+ transactions.sort((a, b) => b.createdAt - a.createdAt);
134
133
  this.cache.transactions.set(address, transactions);
135
134
  await this.storage.setItem(`tx:${address}`, JSON.stringify(transactions));
136
135
  }
136
+ async saveTransactions(address, txs) {
137
+ const storedTransactions = await this.getTransactionHistory(address);
138
+ for (const tx of txs) {
139
+ const existing = storedTransactions.findIndex((t) => t.key === tx.key);
140
+ if (existing !== -1) {
141
+ storedTransactions[existing] = tx;
142
+ }
143
+ else {
144
+ storedTransactions.push(tx);
145
+ }
146
+ }
147
+ this.cache.transactions.set(address, storedTransactions);
148
+ await this.storage.setItem(`tx:${address}`, JSON.stringify(storedTransactions));
149
+ }
150
+ async clearTransactions(address) {
151
+ this.cache.transactions.set(address, []);
152
+ await this.storage.removeItem(`tx:${address}`);
153
+ }
137
154
  async getWalletState() {
138
155
  if (this.cache.walletState !== null ||
139
156
  this.cache.initialized.has("walletState")) {
@@ -69,4 +69,12 @@ var Request;
69
69
  return message.type === "GET_STATUS";
70
70
  }
71
71
  Request.isGetStatus = isGetStatus;
72
+ function isClear(message) {
73
+ return message.type === "CLEAR";
74
+ }
75
+ Request.isClear = isClear;
76
+ function isReloadWallet(message) {
77
+ return message.type === "RELOAD_WALLET";
78
+ }
79
+ Request.isReloadWallet = isReloadWallet;
72
80
  })(Request || (exports.Request = Request = {}));
@@ -175,4 +175,16 @@ var Response;
175
175
  };
176
176
  }
177
177
  Response.clearResponse = clearResponse;
178
+ function isWalletReloaded(response) {
179
+ return response.type === "WALLET_RELOADED";
180
+ }
181
+ Response.isWalletReloaded = isWalletReloaded;
182
+ function walletReloaded(id, success) {
183
+ return {
184
+ type: "WALLET_RELOADED",
185
+ success,
186
+ id,
187
+ };
188
+ }
189
+ Response.walletReloaded = walletReloaded;
178
190
  })(Response || (exports.Response = Response = {}));
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.setupServiceWorker = setupServiceWorker;
4
+ exports.extendVirtualCoin = extendVirtualCoin;
4
5
  /**
5
6
  * setupServiceWorker sets up the service worker.
6
7
  * @param path - the path to the service worker script
@@ -47,3 +48,11 @@ async function setupServiceWorker(path) {
47
48
  navigator.serviceWorker.addEventListener("error", onError);
48
49
  });
49
50
  }
51
+ function extendVirtualCoin(wallet, vtxo) {
52
+ return {
53
+ ...vtxo,
54
+ forfeitTapLeafScript: wallet.offchainTapscript.forfeit(),
55
+ intentTapLeafScript: wallet.offchainTapscript.exit(),
56
+ tapTree: wallet.offchainTapscript.encode(),
57
+ };
58
+ }
@@ -8,10 +8,7 @@ const walletRepository_1 = require("../../repositories/walletRepository");
8
8
  const contractRepository_1 = require("../../repositories/contractRepository");
9
9
  const utils_1 = require("./utils");
10
10
  const isPrivateKeyIdentity = (identity) => {
11
- return (identity.toHex !== undefined &&
12
- typeof identity.toHex === "function" &&
13
- typeof identity.toHex() === "string" &&
14
- identity.toHex().length > 0);
11
+ return typeof identity.toHex === "function";
15
12
  };
16
13
  class UnexpectedResponseError extends Error {
17
14
  constructor(response) {
@@ -290,6 +287,17 @@ class ServiceWorkerWallet {
290
287
  throw new Error(`Settlement failed: ${error}`);
291
288
  }
292
289
  }
290
+ async reload() {
291
+ const message = {
292
+ type: "RELOAD_WALLET",
293
+ id: getRandomId(),
294
+ };
295
+ const response = await this.sendMessage(message);
296
+ if (response_1.Response.isWalletReloaded(response)) {
297
+ return response.success;
298
+ }
299
+ throw new UnexpectedResponseError(response);
300
+ }
293
301
  }
294
302
  exports.ServiceWorkerWallet = ServiceWorkerWallet;
295
303
  function getRandomId() {
@@ -13,6 +13,7 @@ const indexer_1 = require("../../providers/indexer");
13
13
  const base_1 = require("@scure/base");
14
14
  const indexedDB_1 = require("../../storage/indexedDB");
15
15
  const walletRepository_1 = require("../../repositories/walletRepository");
16
+ const utils_1 = require("./utils");
16
17
  /**
17
18
  * Worker is a class letting to interact with ServiceWorkerWallet from the client
18
19
  * it aims to be run in a service worker context
@@ -41,7 +42,7 @@ class Worker {
41
42
  return [];
42
43
  const address = await this.wallet.getAddress();
43
44
  const allVtxos = await this.walletRepository.getVtxos(address);
44
- return allVtxos.filter((vtxo) => vtxo.virtualStatus.state === "swept" && (0, __1.isSpendable)(vtxo));
45
+ return allVtxos.filter((vtxo) => vtxo.virtualStatus.state === "swept");
45
46
  }
46
47
  /**
47
48
  * Get all vtxos categorized by type
@@ -72,19 +73,15 @@ class Worker {
72
73
  }
73
74
  }
74
75
  async clear() {
75
- if (this.vtxoSubscription) {
76
- this.vtxoSubscription.abort();
77
- }
76
+ if (this.incomingFundsSubscription)
77
+ this.incomingFundsSubscription();
78
78
  // Clear storage - this replaces vtxoRepository.close()
79
79
  await this.storage.clear();
80
80
  this.wallet = undefined;
81
81
  this.arkProvider = undefined;
82
82
  this.indexerProvider = undefined;
83
- this.vtxoSubscription = undefined;
84
83
  }
85
84
  async reload() {
86
- if (this.vtxoSubscription)
87
- this.vtxoSubscription.abort();
88
85
  await this.onWalletInitialized();
89
86
  }
90
87
  async onWalletInitialized() {
@@ -95,58 +92,46 @@ class Worker {
95
92
  !this.wallet.boardingTapscript) {
96
93
  return;
97
94
  }
98
- const encodedOffchainTapscript = this.wallet.offchainTapscript.encode();
99
- const forfeit = this.wallet.offchainTapscript.forfeit();
100
- const exit = this.wallet.offchainTapscript.exit();
95
+ // Get public key script and set the initial vtxos state
101
96
  const script = base_1.hex.encode(this.wallet.offchainTapscript.pkScript);
102
- // set the initial vtxos state
103
97
  const response = await this.indexerProvider.getVtxos({
104
98
  scripts: [script],
105
99
  });
106
- const vtxos = response.vtxos.map((vtxo) => ({
107
- ...vtxo,
108
- forfeitTapLeafScript: forfeit,
109
- intentTapLeafScript: exit,
110
- tapTree: encodedOffchainTapscript,
111
- }));
100
+ const vtxos = response.vtxos.map((vtxo) => (0, utils_1.extendVirtualCoin)(this.wallet, vtxo));
112
101
  // Get wallet address and save vtxos using unified repository
113
102
  const address = await this.wallet.getAddress();
114
103
  await this.walletRepository.saveVtxos(address, vtxos);
115
- this.processVtxoSubscription({
116
- script,
117
- vtxoScript: this.wallet.offchainTapscript,
118
- });
119
- }
120
- async processVtxoSubscription({ script, vtxoScript, }) {
121
- try {
122
- const forfeitTapLeafScript = vtxoScript.forfeit();
123
- const intentTapLeafScript = vtxoScript.exit();
124
- const abortController = new AbortController();
125
- const subscriptionId = await this.indexerProvider.subscribeForScripts([script]);
126
- const subscription = this.indexerProvider.getSubscription(subscriptionId, abortController.signal);
127
- this.vtxoSubscription = abortController;
128
- const tapTree = vtxoScript.encode();
129
- for await (const update of subscription) {
130
- const vtxos = [...update.newVtxos, ...update.spentVtxos];
131
- if (vtxos.length === 0) {
132
- continue;
133
- }
134
- const extendedVtxos = vtxos.map((vtxo) => ({
135
- ...vtxo,
136
- forfeitTapLeafScript,
137
- intentTapLeafScript,
138
- tapTree,
139
- }));
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", "");
104
+ // Get transaction history to cache boarding txs
105
+ const txs = await this.wallet.getTransactionHistory();
106
+ if (txs)
107
+ await this.walletRepository.saveTransactions(address, txs);
108
+ // stop previous subscriptions if any
109
+ if (this.incomingFundsSubscription)
110
+ this.incomingFundsSubscription();
111
+ // subscribe for incoming funds and notify all clients when new funds arrive
112
+ this.incomingFundsSubscription = await this.wallet.notifyIncomingFunds(async (funds) => {
113
+ if (funds.type === "vtxo") {
114
+ const newVtxos = funds.newVtxos.length > 0
115
+ ? funds.newVtxos.map((vtxo) => (0, utils_1.extendVirtualCoin)(this.wallet, vtxo))
116
+ : [];
117
+ const spentVtxos = funds.spentVtxos.length > 0
118
+ ? funds.spentVtxos.map((vtxo) => (0, utils_1.extendVirtualCoin)(this.wallet, vtxo))
119
+ : [];
120
+ if ([...newVtxos, ...spentVtxos].length === 0)
121
+ return;
122
+ // save vtxos using unified repository
123
+ await this.walletRepository.saveVtxos(address, [
124
+ ...newVtxos,
125
+ ...spentVtxos,
126
+ ]);
127
+ // notify all clients about the vtxo update
128
+ this.sendMessageToAllClients("VTXO_UPDATE", JSON.stringify({ newVtxos, spentVtxos }));
145
129
  }
146
- }
147
- catch (error) {
148
- console.error("Error processing address updates:", error);
149
- }
130
+ if (funds.type === "utxo" && funds.coins.length > 0) {
131
+ // notify all clients about the utxo update
132
+ this.sendMessageToAllClients("UTXO_UPDATE", JSON.stringify(funds.coins));
133
+ }
134
+ });
150
135
  }
151
136
  async handleClear(event) {
152
137
  await this.clear();
@@ -374,7 +359,11 @@ class Worker {
374
359
  if (!this.wallet)
375
360
  throw new Error("Wallet not initialized");
376
361
  // exclude subdust is we don't want recoverable
377
- vtxos = vtxos.filter((v) => !(0, __1.isSubdust)(v, this.wallet.dustAmount));
362
+ const dustAmount = this.wallet?.dustAmount;
363
+ vtxos =
364
+ dustAmount == null
365
+ ? vtxos
366
+ : vtxos.filter((v) => !(0, __1.isSubdust)(v, dustAmount));
378
367
  }
379
368
  if (message.filter?.withRecoverable) {
380
369
  // get also swept and spendable vtxos
@@ -518,6 +507,10 @@ class Worker {
518
507
  await this.handleClear(event);
519
508
  break;
520
509
  }
510
+ case "RELOAD_WALLET": {
511
+ await this.handleReloadWallet(event);
512
+ break;
513
+ }
521
514
  default:
522
515
  event.source?.postMessage(response_1.Response.error(message.id, "Unknown message type"));
523
516
  }
@@ -534,5 +527,27 @@ class Worker {
534
527
  });
535
528
  });
536
529
  }
530
+ async handleReloadWallet(event) {
531
+ const message = event.data;
532
+ console.log("RELOAD_WALLET message received", message);
533
+ if (!request_1.Request.isReloadWallet(message)) {
534
+ console.error("Invalid RELOAD_WALLET message format", message);
535
+ event.source?.postMessage(response_1.Response.error(message.id, "Invalid RELOAD_WALLET message format"));
536
+ return;
537
+ }
538
+ if (!this.wallet) {
539
+ console.error("Wallet not initialized");
540
+ event.source?.postMessage(response_1.Response.walletReloaded(message.id, false));
541
+ return;
542
+ }
543
+ try {
544
+ await this.onWalletInitialized();
545
+ event.source?.postMessage(response_1.Response.walletReloaded(message.id, true));
546
+ }
547
+ catch (error) {
548
+ console.error("Error reloading wallet:", error);
549
+ event.source?.postMessage(response_1.Response.walletReloaded(message.id, false));
550
+ }
551
+ }
537
552
  }
538
553
  exports.Worker = Worker;
@@ -60,6 +60,7 @@ const txTree_1 = require("../tree/txTree");
60
60
  const inMemory_1 = require("../storage/inMemory");
61
61
  const walletRepository_1 = require("../repositories/walletRepository");
62
62
  const contractRepository_1 = require("../repositories/contractRepository");
63
+ const utils_1 = require("./serviceWorker/utils");
63
64
  /**
64
65
  * Main wallet implementation for Bitcoin transactions with Ark protocol support.
65
66
  * The wallet does not store any data locally and relies on Ark and onchain
@@ -678,7 +679,8 @@ class Wallet {
678
679
  if (update.newVtxos?.length > 0) {
679
680
  eventCallback({
680
681
  type: "vtxo",
681
- vtxos: update.newVtxos,
682
+ newVtxos: update.newVtxos.map((vtxo) => (0, utils_1.extendVirtualCoin)(this, vtxo)),
683
+ spentVtxos: update.spentVtxos.map((vtxo) => (0, utils_1.extendVirtualCoin)(this, vtxo)),
682
684
  });
683
685
  }
684
686
  }
@@ -20,6 +20,7 @@ export const ESPLORA_URL = {
20
20
  export class EsploraProvider {
21
21
  constructor(baseUrl) {
22
22
  this.baseUrl = baseUrl;
23
+ this.polling = false;
23
24
  }
24
25
  async getCoins(address) {
25
26
  const response = await fetch(`${this.baseUrl}/address/${address}/utxo`);
@@ -90,6 +91,9 @@ export class EsploraProvider {
90
91
  let intervalId = null;
91
92
  const wsUrl = this.baseUrl.replace(/^http(s)?:/, "ws$1:") + "/v1/ws";
92
93
  const poll = async () => {
94
+ if (this.polling)
95
+ return;
96
+ this.polling = true;
93
97
  // websocket is not reliable, so we will fallback to polling
94
98
  const pollingInterval = 5000; // 5 seconds
95
99
  const getAllTxs = () => {
@@ -99,19 +103,19 @@ export class EsploraProvider {
99
103
  const initialTxs = await getAllTxs();
100
104
  // we use block_time in key to also notify when a transaction is confirmed
101
105
  const txKey = (tx) => `${tx.txid}_${tx.status.block_time}`;
106
+ // create a set of existing transactions to avoid duplicates
107
+ const existingTxs = new Set(initialTxs.map(txKey));
102
108
  // polling for new transactions
103
109
  intervalId = setInterval(async () => {
104
110
  try {
105
111
  // get current transactions
106
112
  // we will compare with initialTxs to find new ones
107
113
  const currentTxs = await getAllTxs();
108
- // create a set of existing transactions to avoid duplicates
109
- const existingTxs = new Set(initialTxs.map(txKey));
110
114
  // filter out transactions that are already in initialTxs
111
115
  const newTxs = currentTxs.filter((tx) => !existingTxs.has(txKey(tx)));
112
116
  if (newTxs.length > 0) {
113
117
  // Update the tracking set instead of growing the array
114
- initialTxs.push(...newTxs);
118
+ newTxs.forEach((tx) => existingTxs.add(txKey(tx)));
115
119
  callback(newTxs);
116
120
  }
117
121
  }
@@ -172,6 +176,7 @@ export class EsploraProvider {
172
176
  ws.close();
173
177
  if (intervalId)
174
178
  clearInterval(intervalId);
179
+ this.polling = false;
175
180
  };
176
181
  return stopFunc;
177
182
  }
@@ -123,4 +123,8 @@ export class ContractRepositoryImpl {
123
123
  throw error; // Rethrow to notify caller of failure
124
124
  }
125
125
  }
126
+ async clearContractData() {
127
+ await this.storage.clear();
128
+ this.cache.clear();
129
+ }
126
130
  }
@@ -4,8 +4,7 @@ import { TaprootControlBlock } from "@scure/btc-signer";
4
4
  const toHex = (b) => (b ? hex.encode(b) : undefined);
5
5
  const fromHex = (h) => h ? hex.decode(h) : undefined;
6
6
  const serializeTapLeaf = ([cb, s]) => ({
7
- cb: TaprootControlBlock.encode(cb) &&
8
- hex.encode(TaprootControlBlock.encode(cb)),
7
+ cb: hex.encode(TaprootControlBlock.encode(cb)),
9
8
  s: hex.encode(s),
10
9
  });
11
10
  const serializeVtxo = (v) => ({
@@ -50,7 +49,7 @@ export class WalletRepositoryImpl {
50
49
  try {
51
50
  const parsed = JSON.parse(stored);
52
51
  const vtxos = parsed.map(deserializeVtxo);
53
- this.cache.vtxos.set(address, vtxos);
52
+ this.cache.vtxos.set(address, vtxos.slice());
54
53
  return vtxos.slice();
55
54
  }
56
55
  catch (error) {
@@ -68,7 +67,7 @@ export class WalletRepositoryImpl {
68
67
  else {
69
68
  vtxos.push(vtxo);
70
69
  }
71
- this.cache.vtxos.set(address, vtxos);
70
+ this.cache.vtxos.set(address, vtxos.slice());
72
71
  await this.storage.setItem(`vtxos:${address}`, JSON.stringify(vtxos.map(serializeVtxo)));
73
72
  }
74
73
  async saveVtxos(address, vtxos) {
@@ -82,14 +81,14 @@ export class WalletRepositoryImpl {
82
81
  storedVtxos.push(vtxo);
83
82
  }
84
83
  }
85
- this.cache.vtxos.set(address, storedVtxos);
84
+ this.cache.vtxos.set(address, storedVtxos.slice());
86
85
  await this.storage.setItem(`vtxos:${address}`, JSON.stringify(storedVtxos.map(serializeVtxo)));
87
86
  }
88
87
  async removeVtxo(address, vtxoId) {
89
88
  const vtxos = await this.getVtxos(address);
90
89
  const [txid, vout] = vtxoId.split(":");
91
- const filtered = vtxos.filter((v) => !(v.txid === txid && v.vout === parseInt(vout)));
92
- this.cache.vtxos.set(address, filtered);
90
+ const filtered = vtxos.filter((v) => !(v.txid === txid && v.vout === parseInt(vout, 10)));
91
+ this.cache.vtxos.set(address, filtered.slice());
93
92
  await this.storage.setItem(`vtxos:${address}`, JSON.stringify(filtered.map(serializeVtxo)));
94
93
  }
95
94
  async clearVtxos(address) {
@@ -119,18 +118,36 @@ export class WalletRepositoryImpl {
119
118
  }
120
119
  async saveTransaction(address, tx) {
121
120
  const transactions = await this.getTransactionHistory(address);
122
- const existing = transactions.findIndex((t) => t.id === tx.id);
121
+ const existing = transactions.findIndex((t) => t.key === tx.key);
123
122
  if (existing !== -1) {
124
123
  transactions[existing] = tx;
125
124
  }
126
125
  else {
127
126
  transactions.push(tx);
128
- // Sort by timestamp descending
129
- transactions.sort((a, b) => b.timestamp - a.timestamp);
130
127
  }
128
+ // Sort by createdAt descending
129
+ transactions.sort((a, b) => b.createdAt - a.createdAt);
131
130
  this.cache.transactions.set(address, transactions);
132
131
  await this.storage.setItem(`tx:${address}`, JSON.stringify(transactions));
133
132
  }
133
+ async saveTransactions(address, txs) {
134
+ const storedTransactions = await this.getTransactionHistory(address);
135
+ for (const tx of txs) {
136
+ const existing = storedTransactions.findIndex((t) => t.key === tx.key);
137
+ if (existing !== -1) {
138
+ storedTransactions[existing] = tx;
139
+ }
140
+ else {
141
+ storedTransactions.push(tx);
142
+ }
143
+ }
144
+ this.cache.transactions.set(address, storedTransactions);
145
+ await this.storage.setItem(`tx:${address}`, JSON.stringify(storedTransactions));
146
+ }
147
+ async clearTransactions(address) {
148
+ this.cache.transactions.set(address, []);
149
+ await this.storage.removeItem(`tx:${address}`);
150
+ }
134
151
  async getWalletState() {
135
152
  if (this.cache.walletState !== null ||
136
153
  this.cache.initialized.has("walletState")) {
@@ -66,4 +66,12 @@ export var Request;
66
66
  return message.type === "GET_STATUS";
67
67
  }
68
68
  Request.isGetStatus = isGetStatus;
69
+ function isClear(message) {
70
+ return message.type === "CLEAR";
71
+ }
72
+ Request.isClear = isClear;
73
+ function isReloadWallet(message) {
74
+ return message.type === "RELOAD_WALLET";
75
+ }
76
+ Request.isReloadWallet = isReloadWallet;
69
77
  })(Request || (Request = {}));
@@ -172,4 +172,16 @@ export var Response;
172
172
  };
173
173
  }
174
174
  Response.clearResponse = clearResponse;
175
+ function isWalletReloaded(response) {
176
+ return response.type === "WALLET_RELOADED";
177
+ }
178
+ Response.isWalletReloaded = isWalletReloaded;
179
+ function walletReloaded(id, success) {
180
+ return {
181
+ type: "WALLET_RELOADED",
182
+ success,
183
+ id,
184
+ };
185
+ }
186
+ Response.walletReloaded = walletReloaded;
175
187
  })(Response || (Response = {}));
@@ -44,3 +44,11 @@ export async function setupServiceWorker(path) {
44
44
  navigator.serviceWorker.addEventListener("error", onError);
45
45
  });
46
46
  }
47
+ export function extendVirtualCoin(wallet, vtxo) {
48
+ return {
49
+ ...vtxo,
50
+ forfeitTapLeafScript: wallet.offchainTapscript.forfeit(),
51
+ intentTapLeafScript: wallet.offchainTapscript.exit(),
52
+ tapTree: wallet.offchainTapscript.encode(),
53
+ };
54
+ }
@@ -5,10 +5,7 @@ import { WalletRepositoryImpl } from '../../repositories/walletRepository.js';
5
5
  import { ContractRepositoryImpl } from '../../repositories/contractRepository.js';
6
6
  import { setupServiceWorker } from './utils.js';
7
7
  const isPrivateKeyIdentity = (identity) => {
8
- return (identity.toHex !== undefined &&
9
- typeof identity.toHex === "function" &&
10
- typeof identity.toHex() === "string" &&
11
- identity.toHex().length > 0);
8
+ return typeof identity.toHex === "function";
12
9
  };
13
10
  class UnexpectedResponseError extends Error {
14
11
  constructor(response) {
@@ -287,6 +284,17 @@ export class ServiceWorkerWallet {
287
284
  throw new Error(`Settlement failed: ${error}`);
288
285
  }
289
286
  }
287
+ async reload() {
288
+ const message = {
289
+ type: "RELOAD_WALLET",
290
+ id: getRandomId(),
291
+ };
292
+ const response = await this.sendMessage(message);
293
+ if (Response.isWalletReloaded(response)) {
294
+ return response.success;
295
+ }
296
+ throw new UnexpectedResponseError(response);
297
+ }
290
298
  }
291
299
  function getRandomId() {
292
300
  const randomValue = crypto.getRandomValues(new Uint8Array(16));
@@ -10,6 +10,7 @@ import { RestIndexerProvider } from '../../providers/indexer.js';
10
10
  import { hex } from "@scure/base";
11
11
  import { IndexedDBStorageAdapter } from '../../storage/indexedDB.js';
12
12
  import { WalletRepositoryImpl, } from '../../repositories/walletRepository.js';
13
+ import { extendVirtualCoin } from './utils.js';
13
14
  /**
14
15
  * Worker is a class letting to interact with ServiceWorkerWallet from the client
15
16
  * it aims to be run in a service worker context
@@ -38,7 +39,7 @@ export class Worker {
38
39
  return [];
39
40
  const address = await this.wallet.getAddress();
40
41
  const allVtxos = await this.walletRepository.getVtxos(address);
41
- return allVtxos.filter((vtxo) => vtxo.virtualStatus.state === "swept" && isSpendable(vtxo));
42
+ return allVtxos.filter((vtxo) => vtxo.virtualStatus.state === "swept");
42
43
  }
43
44
  /**
44
45
  * Get all vtxos categorized by type
@@ -69,19 +70,15 @@ export class Worker {
69
70
  }
70
71
  }
71
72
  async clear() {
72
- if (this.vtxoSubscription) {
73
- this.vtxoSubscription.abort();
74
- }
73
+ if (this.incomingFundsSubscription)
74
+ this.incomingFundsSubscription();
75
75
  // Clear storage - this replaces vtxoRepository.close()
76
76
  await this.storage.clear();
77
77
  this.wallet = undefined;
78
78
  this.arkProvider = undefined;
79
79
  this.indexerProvider = undefined;
80
- this.vtxoSubscription = undefined;
81
80
  }
82
81
  async reload() {
83
- if (this.vtxoSubscription)
84
- this.vtxoSubscription.abort();
85
82
  await this.onWalletInitialized();
86
83
  }
87
84
  async onWalletInitialized() {
@@ -92,58 +89,46 @@ export class Worker {
92
89
  !this.wallet.boardingTapscript) {
93
90
  return;
94
91
  }
95
- const encodedOffchainTapscript = this.wallet.offchainTapscript.encode();
96
- const forfeit = this.wallet.offchainTapscript.forfeit();
97
- const exit = this.wallet.offchainTapscript.exit();
92
+ // Get public key script and set the initial vtxos state
98
93
  const script = hex.encode(this.wallet.offchainTapscript.pkScript);
99
- // set the initial vtxos state
100
94
  const response = await this.indexerProvider.getVtxos({
101
95
  scripts: [script],
102
96
  });
103
- const vtxos = response.vtxos.map((vtxo) => ({
104
- ...vtxo,
105
- forfeitTapLeafScript: forfeit,
106
- intentTapLeafScript: exit,
107
- tapTree: encodedOffchainTapscript,
108
- }));
97
+ const vtxos = response.vtxos.map((vtxo) => extendVirtualCoin(this.wallet, vtxo));
109
98
  // Get wallet address and save vtxos using unified repository
110
99
  const address = await this.wallet.getAddress();
111
100
  await this.walletRepository.saveVtxos(address, vtxos);
112
- this.processVtxoSubscription({
113
- script,
114
- vtxoScript: this.wallet.offchainTapscript,
115
- });
116
- }
117
- async processVtxoSubscription({ script, vtxoScript, }) {
118
- try {
119
- const forfeitTapLeafScript = vtxoScript.forfeit();
120
- const intentTapLeafScript = vtxoScript.exit();
121
- const abortController = new AbortController();
122
- const subscriptionId = await this.indexerProvider.subscribeForScripts([script]);
123
- const subscription = this.indexerProvider.getSubscription(subscriptionId, abortController.signal);
124
- this.vtxoSubscription = abortController;
125
- const tapTree = vtxoScript.encode();
126
- for await (const update of subscription) {
127
- const vtxos = [...update.newVtxos, ...update.spentVtxos];
128
- if (vtxos.length === 0) {
129
- continue;
130
- }
131
- const extendedVtxos = vtxos.map((vtxo) => ({
132
- ...vtxo,
133
- forfeitTapLeafScript,
134
- intentTapLeafScript,
135
- tapTree,
136
- }));
137
- // Get wallet address and save vtxos using unified repository
138
- const address = await this.wallet.getAddress();
139
- await this.walletRepository.saveVtxos(address, extendedVtxos);
140
- // Notify all clients about the vtxo update
141
- this.sendMessageToAllClients("VTXO_UPDATE", "");
101
+ // Get transaction history to cache boarding txs
102
+ const txs = await this.wallet.getTransactionHistory();
103
+ if (txs)
104
+ await this.walletRepository.saveTransactions(address, txs);
105
+ // stop previous subscriptions if any
106
+ if (this.incomingFundsSubscription)
107
+ this.incomingFundsSubscription();
108
+ // subscribe for incoming funds and notify all clients when new funds arrive
109
+ this.incomingFundsSubscription = await this.wallet.notifyIncomingFunds(async (funds) => {
110
+ if (funds.type === "vtxo") {
111
+ const newVtxos = funds.newVtxos.length > 0
112
+ ? funds.newVtxos.map((vtxo) => extendVirtualCoin(this.wallet, vtxo))
113
+ : [];
114
+ const spentVtxos = funds.spentVtxos.length > 0
115
+ ? funds.spentVtxos.map((vtxo) => extendVirtualCoin(this.wallet, vtxo))
116
+ : [];
117
+ if ([...newVtxos, ...spentVtxos].length === 0)
118
+ return;
119
+ // save vtxos using unified repository
120
+ await this.walletRepository.saveVtxos(address, [
121
+ ...newVtxos,
122
+ ...spentVtxos,
123
+ ]);
124
+ // notify all clients about the vtxo update
125
+ this.sendMessageToAllClients("VTXO_UPDATE", JSON.stringify({ newVtxos, spentVtxos }));
142
126
  }
143
- }
144
- catch (error) {
145
- console.error("Error processing address updates:", error);
146
- }
127
+ if (funds.type === "utxo" && funds.coins.length > 0) {
128
+ // notify all clients about the utxo update
129
+ this.sendMessageToAllClients("UTXO_UPDATE", JSON.stringify(funds.coins));
130
+ }
131
+ });
147
132
  }
148
133
  async handleClear(event) {
149
134
  await this.clear();
@@ -371,7 +356,11 @@ export class Worker {
371
356
  if (!this.wallet)
372
357
  throw new Error("Wallet not initialized");
373
358
  // exclude subdust is we don't want recoverable
374
- vtxos = vtxos.filter((v) => !isSubdust(v, this.wallet.dustAmount));
359
+ const dustAmount = this.wallet?.dustAmount;
360
+ vtxos =
361
+ dustAmount == null
362
+ ? vtxos
363
+ : vtxos.filter((v) => !isSubdust(v, dustAmount));
375
364
  }
376
365
  if (message.filter?.withRecoverable) {
377
366
  // get also swept and spendable vtxos
@@ -515,6 +504,10 @@ export class Worker {
515
504
  await this.handleClear(event);
516
505
  break;
517
506
  }
507
+ case "RELOAD_WALLET": {
508
+ await this.handleReloadWallet(event);
509
+ break;
510
+ }
518
511
  default:
519
512
  event.source?.postMessage(Response.error(message.id, "Unknown message type"));
520
513
  }
@@ -531,4 +524,26 @@ export class Worker {
531
524
  });
532
525
  });
533
526
  }
527
+ async handleReloadWallet(event) {
528
+ const message = event.data;
529
+ console.log("RELOAD_WALLET message received", message);
530
+ if (!Request.isReloadWallet(message)) {
531
+ console.error("Invalid RELOAD_WALLET message format", message);
532
+ event.source?.postMessage(Response.error(message.id, "Invalid RELOAD_WALLET message format"));
533
+ return;
534
+ }
535
+ if (!this.wallet) {
536
+ console.error("Wallet not initialized");
537
+ event.source?.postMessage(Response.walletReloaded(message.id, false));
538
+ return;
539
+ }
540
+ try {
541
+ await this.onWalletInitialized();
542
+ event.source?.postMessage(Response.walletReloaded(message.id, true));
543
+ }
544
+ catch (error) {
545
+ console.error("Error reloading wallet:", error);
546
+ event.source?.postMessage(Response.walletReloaded(message.id, false));
547
+ }
548
+ }
534
549
  }
@@ -23,6 +23,7 @@ import { TxTree } from '../tree/txTree.js';
23
23
  import { InMemoryStorageAdapter } from '../storage/inMemory.js';
24
24
  import { WalletRepositoryImpl, } from '../repositories/walletRepository.js';
25
25
  import { ContractRepositoryImpl, } from '../repositories/contractRepository.js';
26
+ import { extendVirtualCoin } from './serviceWorker/utils.js';
26
27
  /**
27
28
  * Main wallet implementation for Bitcoin transactions with Ark protocol support.
28
29
  * The wallet does not store any data locally and relies on Ark and onchain
@@ -641,7 +642,8 @@ export class Wallet {
641
642
  if (update.newVtxos?.length > 0) {
642
643
  eventCallback({
643
644
  type: "vtxo",
644
- vtxos: update.newVtxos,
645
+ newVtxos: update.newVtxos.map((vtxo) => extendVirtualCoin(this, vtxo)),
646
+ spentVtxos: update.spentVtxos.map((vtxo) => extendVirtualCoin(this, vtxo)),
645
647
  });
646
648
  }
647
649
  }
@@ -49,6 +49,7 @@ export interface OnchainProvider {
49
49
  */
50
50
  export declare class EsploraProvider implements OnchainProvider {
51
51
  private baseUrl;
52
+ private polling;
52
53
  constructor(baseUrl: string);
53
54
  getCoins(address: string): Promise<Coin[]>;
54
55
  getFeeRate(): Promise<number | undefined>;
@@ -3,6 +3,7 @@ export interface ContractRepository {
3
3
  getContractData<T>(contractId: string, key: string): Promise<T | null>;
4
4
  setContractData<T>(contractId: string, key: string, data: T): Promise<void>;
5
5
  deleteContractData(contractId: string, key: string): Promise<void>;
6
+ clearContractData(): Promise<void>;
6
7
  getContractCollection<T>(contractType: string): Promise<ReadonlyArray<T>>;
7
8
  saveToContractCollection<T, K extends keyof T>(contractType: string, item: T, idField: K): Promise<void>;
8
9
  removeFromContractCollection<T, K extends keyof T>(contractType: string, id: T[K], idField: K): Promise<void>;
@@ -17,4 +18,5 @@ export declare class ContractRepositoryImpl implements ContractRepository {
17
18
  getContractCollection<T>(contractType: string): Promise<ReadonlyArray<T>>;
18
19
  saveToContractCollection<T, K extends keyof T>(contractType: string, item: T, idField: K): Promise<void>;
19
20
  removeFromContractCollection<T, K extends keyof T>(contractType: string, id: T[K], idField: K): Promise<void>;
21
+ clearContractData(): Promise<void>;
20
22
  }
@@ -1,24 +1,19 @@
1
1
  import { StorageAdapter } from "../storage";
2
- import { ExtendedVirtualCoin } from "../wallet";
2
+ import { ArkTransaction, ExtendedVirtualCoin } from "../wallet";
3
3
  export interface WalletState {
4
4
  lastSyncTime?: number;
5
5
  settings?: Record<string, any>;
6
6
  }
7
- export interface Transaction {
8
- id: string;
9
- timestamp: number;
10
- amount: number;
11
- type: "send" | "receive";
12
- status: "pending" | "confirmed" | "failed";
13
- }
14
7
  export interface WalletRepository {
15
8
  getVtxos(address: string): Promise<ExtendedVirtualCoin[]>;
16
9
  saveVtxo(address: string, vtxo: ExtendedVirtualCoin): Promise<void>;
17
10
  saveVtxos(address: string, vtxos: ExtendedVirtualCoin[]): Promise<void>;
18
11
  removeVtxo(address: string, vtxoId: string): Promise<void>;
19
12
  clearVtxos(address: string): Promise<void>;
20
- getTransactionHistory(address: string): Promise<Transaction[]>;
21
- saveTransaction(address: string, tx: Transaction): Promise<void>;
13
+ getTransactionHistory(address: string): Promise<ArkTransaction[]>;
14
+ saveTransaction(address: string, tx: ArkTransaction): Promise<void>;
15
+ saveTransactions(address: string, txs: ArkTransaction[]): Promise<void>;
16
+ clearTransactions(address: string): Promise<void>;
22
17
  getWalletState(): Promise<WalletState | null>;
23
18
  saveWalletState(state: WalletState): Promise<void>;
24
19
  }
@@ -31,8 +26,10 @@ export declare class WalletRepositoryImpl implements WalletRepository {
31
26
  saveVtxos(address: string, vtxos: ExtendedVirtualCoin[]): Promise<void>;
32
27
  removeVtxo(address: string, vtxoId: string): Promise<void>;
33
28
  clearVtxos(address: string): Promise<void>;
34
- getTransactionHistory(address: string): Promise<Transaction[]>;
35
- saveTransaction(address: string, tx: Transaction): Promise<void>;
29
+ getTransactionHistory(address: string): Promise<ArkTransaction[]>;
30
+ saveTransaction(address: string, tx: ArkTransaction): Promise<void>;
31
+ saveTransactions(address: string, txs: ArkTransaction[]): Promise<void>;
32
+ clearTransactions(address: string): Promise<void>;
36
33
  getWalletState(): Promise<WalletState | null>;
37
34
  saveWalletState(state: WalletState): Promise<void>;
38
35
  }
@@ -3,7 +3,7 @@ import { SettleParams, SendBitcoinParams, GetVtxosFilter } from "..";
3
3
  * Request is the namespace that contains the request types for the service worker.
4
4
  */
5
5
  export declare namespace Request {
6
- type Type = "INIT_WALLET" | "SETTLE" | "GET_ADDRESS" | "GET_BOARDING_ADDRESS" | "GET_BALANCE" | "GET_VTXOS" | "GET_VIRTUAL_COINS" | "GET_BOARDING_UTXOS" | "SEND_BITCOIN" | "GET_TRANSACTION_HISTORY" | "GET_STATUS" | "CLEAR";
6
+ type Type = "INIT_WALLET" | "RELOAD_WALLET" | "SETTLE" | "GET_ADDRESS" | "GET_BOARDING_ADDRESS" | "GET_BALANCE" | "GET_VTXOS" | "GET_VIRTUAL_COINS" | "GET_BOARDING_UTXOS" | "SEND_BITCOIN" | "GET_TRANSACTION_HISTORY" | "GET_STATUS" | "CLEAR";
7
7
  interface Base {
8
8
  type: Type;
9
9
  id: string;
@@ -62,4 +62,9 @@ export declare namespace Request {
62
62
  interface Clear extends Base {
63
63
  type: "CLEAR";
64
64
  }
65
+ function isClear(message: Base): message is Clear;
66
+ interface ReloadWallet extends Base {
67
+ type: "RELOAD_WALLET";
68
+ }
69
+ function isReloadWallet(message: Base): message is ReloadWallet;
65
70
  }
@@ -4,7 +4,7 @@ import { SettlementEvent } from "../../providers/ark";
4
4
  * Response is the namespace that contains the response types for the service worker.
5
5
  */
6
6
  export declare namespace Response {
7
- type Type = "WALLET_INITIALIZED" | "SETTLE_EVENT" | "SETTLE_SUCCESS" | "ADDRESS" | "BOARDING_ADDRESS" | "BALANCE" | "VTXOS" | "VIRTUAL_COINS" | "BOARDING_UTXOS" | "SEND_BITCOIN_SUCCESS" | "TRANSACTION_HISTORY" | "WALLET_STATUS" | "ERROR" | "CLEAR_RESPONSE";
7
+ type Type = "WALLET_INITIALIZED" | "WALLET_RELOADED" | "SETTLE_EVENT" | "SETTLE_SUCCESS" | "ADDRESS" | "BOARDING_ADDRESS" | "BALANCE" | "VTXOS" | "VIRTUAL_COINS" | "BOARDING_UTXOS" | "SEND_BITCOIN_SUCCESS" | "TRANSACTION_HISTORY" | "WALLET_STATUS" | "ERROR" | "CLEAR_RESPONSE";
8
8
  interface Base {
9
9
  type: Type;
10
10
  success: boolean;
@@ -101,4 +101,9 @@ export declare namespace Response {
101
101
  }
102
102
  function isClearResponse(response: Base): response is ClearResponse;
103
103
  function clearResponse(id: string, success: boolean): ClearResponse;
104
+ interface WalletReloaded extends Base {
105
+ type: "WALLET_RELOADED";
106
+ }
107
+ function isWalletReloaded(response: Base): response is WalletReloaded;
108
+ function walletReloaded(id: string, success: boolean): WalletReloaded;
104
109
  }
@@ -1,3 +1,4 @@
1
+ import { ExtendedVirtualCoin, VirtualCoin, Wallet } from "../..";
1
2
  /**
2
3
  * setupServiceWorker sets up the service worker.
3
4
  * @param path - the path to the service worker script
@@ -7,3 +8,4 @@
7
8
  * ```
8
9
  */
9
10
  export declare function setupServiceWorker(path: string): Promise<ServiceWorker>;
11
+ export declare function extendVirtualCoin(wallet: Wallet, vtxo: VirtualCoin): ExtendedVirtualCoin;
@@ -93,5 +93,6 @@ export declare class ServiceWorkerWallet implements IWallet {
93
93
  getVtxos(filter?: GetVtxosFilter): Promise<ExtendedVirtualCoin[]>;
94
94
  sendBitcoin(params: SendBitcoinParams): Promise<string>;
95
95
  settle(params?: SettleParams, callback?: (event: SettlementEvent) => void): Promise<string>;
96
+ reload(): Promise<boolean>;
96
97
  }
97
98
  export {};
@@ -7,7 +7,7 @@ export declare class Worker {
7
7
  private wallet;
8
8
  private arkProvider;
9
9
  private indexerProvider;
10
- private vtxoSubscription;
10
+ private incomingFundsSubscription;
11
11
  private walletRepository;
12
12
  private storage;
13
13
  constructor(messageCallback?: (message: ExtendableMessageEvent) => void);
@@ -27,7 +27,6 @@ export declare class Worker {
27
27
  clear(): Promise<void>;
28
28
  reload(): Promise<void>;
29
29
  private onWalletInitialized;
30
- private processVtxoSubscription;
31
30
  private handleClear;
32
31
  private handleInitWallet;
33
32
  private handleSettle;
@@ -41,4 +40,5 @@ export declare class Worker {
41
40
  private handleGetStatus;
42
41
  private handleMessage;
43
42
  private sendMessageToAllClients;
43
+ private handleReloadWallet;
44
44
  }
@@ -4,7 +4,7 @@ import { Network, NetworkName } from "../networks";
4
4
  import { OnchainProvider } from "../providers/onchain";
5
5
  import { SettlementEvent, ArkProvider } from "../providers/ark";
6
6
  import { Identity } from "../identity";
7
- import { ArkTransaction, Coin, ExtendedCoin, ExtendedVirtualCoin, GetVtxosFilter, IWallet, SendBitcoinParams, SettleParams, VirtualCoin, WalletBalance, WalletConfig } from ".";
7
+ import { ArkTransaction, Coin, ExtendedCoin, ExtendedVirtualCoin, GetVtxosFilter, IWallet, SendBitcoinParams, SettleParams, WalletBalance, WalletConfig } from ".";
8
8
  import { Bytes } from "@scure/btc-signer/utils.js";
9
9
  import { CSVMultisigTapscript } from "../script/tapscript";
10
10
  import { IndexerProvider } from "../providers/indexer";
@@ -15,7 +15,8 @@ export type IncomingFunds = {
15
15
  coins: Coin[];
16
16
  } | {
17
17
  type: "vtxo";
18
- vtxos: VirtualCoin[];
18
+ newVtxos: ExtendedVirtualCoin[];
19
+ spentVtxos: ExtendedVirtualCoin[];
19
20
  };
20
21
  /**
21
22
  * Main wallet implementation for Bitcoin transactions with Ark protocol support.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arkade-os/sdk",
3
- "version": "0.3.0-alpha.4",
3
+ "version": "0.3.0-alpha.6",
4
4
  "description": "Bitcoin wallet SDK with Taproot and Ark integration",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",