@arkade-os/sdk 0.3.0-alpha.5 → 0.3.0-alpha.7

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.
@@ -52,7 +52,7 @@ class WalletRepositoryImpl {
52
52
  try {
53
53
  const parsed = JSON.parse(stored);
54
54
  const vtxos = parsed.map(deserializeVtxo);
55
- this.cache.vtxos.set(address, vtxos);
55
+ this.cache.vtxos.set(address, vtxos.slice());
56
56
  return vtxos.slice();
57
57
  }
58
58
  catch (error) {
@@ -70,7 +70,7 @@ class WalletRepositoryImpl {
70
70
  else {
71
71
  vtxos.push(vtxo);
72
72
  }
73
- this.cache.vtxos.set(address, vtxos);
73
+ this.cache.vtxos.set(address, vtxos.slice());
74
74
  await this.storage.setItem(`vtxos:${address}`, JSON.stringify(vtxos.map(serializeVtxo)));
75
75
  }
76
76
  async saveVtxos(address, vtxos) {
@@ -84,14 +84,14 @@ class WalletRepositoryImpl {
84
84
  storedVtxos.push(vtxo);
85
85
  }
86
86
  }
87
- this.cache.vtxos.set(address, storedVtxos);
87
+ this.cache.vtxos.set(address, storedVtxos.slice());
88
88
  await this.storage.setItem(`vtxos:${address}`, JSON.stringify(storedVtxos.map(serializeVtxo)));
89
89
  }
90
90
  async removeVtxo(address, vtxoId) {
91
91
  const vtxos = await this.getVtxos(address);
92
92
  const [txid, vout] = vtxoId.split(":");
93
93
  const filtered = vtxos.filter((v) => !(v.txid === txid && v.vout === parseInt(vout, 10)));
94
- this.cache.vtxos.set(address, filtered);
94
+ this.cache.vtxos.set(address, filtered.slice());
95
95
  await this.storage.setItem(`vtxos:${address}`, JSON.stringify(filtered.map(serializeVtxo)));
96
96
  }
97
97
  async clearVtxos(address) {
@@ -2,12 +2,14 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.VtxoScript = void 0;
4
4
  exports.scriptFromTapLeafScript = scriptFromTapLeafScript;
5
+ const btc_signer_1 = require("@scure/btc-signer");
5
6
  const payment_js_1 = require("@scure/btc-signer/payment.js");
7
+ const psbt_js_1 = require("@scure/btc-signer/psbt.js");
6
8
  const utils_js_1 = require("@scure/btc-signer/utils.js");
7
- const address_1 = require("./address");
8
- const script_js_1 = require("@scure/btc-signer/script.js");
9
9
  const base_1 = require("@scure/base");
10
+ const address_1 = require("./address");
10
11
  const tapscript_1 = require("./tapscript");
12
+ const TapTreeCoder = psbt_js_1.PSBTOutput.tapTree[2];
11
13
  function scriptFromTapLeafScript(leaf) {
12
14
  return leaf[1].subarray(0, leaf[1].length - 1); // remove the version byte
13
15
  }
@@ -21,13 +23,14 @@ function scriptFromTapLeafScript(leaf) {
21
23
  */
22
24
  class VtxoScript {
23
25
  static decode(tapTree) {
24
- const leaves = decodeTaprootTree(tapTree);
25
- return new VtxoScript(leaves);
26
+ const leaves = TapTreeCoder.decode(tapTree);
27
+ const scripts = leaves.map((leaf) => leaf.script);
28
+ return new VtxoScript(scripts);
26
29
  }
27
30
  constructor(scripts) {
28
31
  this.scripts = scripts;
29
- const tapTree = (0, payment_js_1.taprootListToTree)(scripts.map((script) => ({ script, leafVersion: payment_js_1.TAP_LEAF_VERSION })));
30
- const payment = (0, payment_js_1.p2tr)(utils_js_1.TAPROOT_UNSPENDABLE_KEY, tapTree, undefined, true);
32
+ const tapTree = (0, btc_signer_1.taprootListToTree)(scripts.map((script) => ({ script, leafVersion: payment_js_1.TAP_LEAF_VERSION })));
33
+ const payment = (0, btc_signer_1.p2tr)(utils_js_1.TAPROOT_UNSPENDABLE_KEY, tapTree, undefined, true);
31
34
  if (!payment.tapLeafScript ||
32
35
  payment.tapLeafScript.length !== scripts.length) {
33
36
  throw new Error("invalid scripts");
@@ -36,17 +39,21 @@ class VtxoScript {
36
39
  this.tweakedPublicKey = payment.tweakedPubkey;
37
40
  }
38
41
  encode() {
39
- const tapTree = encodeTaprootTree(this.scripts);
42
+ const tapTree = TapTreeCoder.encode(this.scripts.map((script) => ({
43
+ depth: 1,
44
+ version: payment_js_1.TAP_LEAF_VERSION,
45
+ script,
46
+ })));
40
47
  return tapTree;
41
48
  }
42
49
  address(prefix, serverPubKey) {
43
50
  return new address_1.ArkAddress(serverPubKey, this.tweakedPublicKey, prefix);
44
51
  }
45
52
  get pkScript() {
46
- return script_js_1.Script.encode(["OP_1", this.tweakedPublicKey]);
53
+ return btc_signer_1.Script.encode(["OP_1", this.tweakedPublicKey]);
47
54
  }
48
55
  onchainAddress(network) {
49
- return (0, payment_js_1.Address)(network).encode({
56
+ return (0, btc_signer_1.Address)(network).encode({
50
57
  type: "tr",
51
58
  pubkey: this.tweakedPublicKey,
52
59
  });
@@ -80,89 +87,3 @@ class VtxoScript {
80
87
  }
81
88
  }
82
89
  exports.VtxoScript = VtxoScript;
83
- function decodeTaprootTree(tapTree) {
84
- let offset = 0;
85
- const scripts = [];
86
- // Read number of leaves
87
- const [numLeaves, numLeavesSize] = decodeCompactSizeUint(tapTree, offset);
88
- offset += numLeavesSize;
89
- // Read each leaf
90
- for (let i = 0; i < numLeaves; i++) {
91
- // Skip depth (1 byte)
92
- offset += 1;
93
- // Skip leaf version (1 byte)
94
- offset += 1;
95
- // Read script length
96
- const [scriptLength, scriptLengthSize] = decodeCompactSizeUint(tapTree, offset);
97
- offset += scriptLengthSize;
98
- // Read script content
99
- const script = tapTree.slice(offset, offset + scriptLength);
100
- scripts.push(script);
101
- offset += scriptLength;
102
- }
103
- return scripts;
104
- }
105
- function decodeCompactSizeUint(data, offset) {
106
- const firstByte = data[offset];
107
- if (firstByte < 0xfd) {
108
- return [firstByte, 1];
109
- }
110
- else if (firstByte === 0xfd) {
111
- const value = new DataView(data.buffer).getUint16(offset + 1, true);
112
- return [value, 3];
113
- }
114
- else if (firstByte === 0xfe) {
115
- const value = new DataView(data.buffer).getUint32(offset + 1, true);
116
- return [value, 5];
117
- }
118
- else {
119
- const value = Number(new DataView(data.buffer).getBigUint64(offset + 1, true));
120
- return [value, 9];
121
- }
122
- }
123
- function encodeTaprootTree(leaves) {
124
- const chunks = [];
125
- // Write number of leaves as compact size uint
126
- chunks.push(encodeCompactSizeUint(leaves.length));
127
- for (const tapscript of leaves) {
128
- // Write depth (always 1 for now)
129
- chunks.push(new Uint8Array([1]));
130
- // Write leaf version (0xc0 for tapscript)
131
- chunks.push(new Uint8Array([0xc0]));
132
- // Write script length and script
133
- chunks.push(encodeCompactSizeUint(tapscript.length));
134
- chunks.push(tapscript);
135
- }
136
- // Concatenate all chunks
137
- const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
138
- const result = new Uint8Array(totalLength);
139
- let offset = 0;
140
- for (const chunk of chunks) {
141
- result.set(chunk, offset);
142
- offset += chunk.length;
143
- }
144
- return result;
145
- }
146
- function encodeCompactSizeUint(value) {
147
- if (value < 0xfd) {
148
- return new Uint8Array([value]);
149
- }
150
- else if (value <= 0xffff) {
151
- const buffer = new Uint8Array(3);
152
- buffer[0] = 0xfd;
153
- new DataView(buffer.buffer).setUint16(1, value, true);
154
- return buffer;
155
- }
156
- else if (value <= 0xffffffff) {
157
- const buffer = new Uint8Array(5);
158
- buffer[0] = 0xfe;
159
- new DataView(buffer.buffer).setUint32(1, value, true);
160
- return buffer;
161
- }
162
- else {
163
- const buffer = new Uint8Array(9);
164
- buffer[0] = 0xff;
165
- new DataView(buffer.buffer).setBigUint64(1, BigInt(value), true);
166
- return buffer;
167
- }
168
- }
@@ -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 = {}));
@@ -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() {
@@ -110,13 +110,22 @@ class Worker {
110
110
  this.incomingFundsSubscription();
111
111
  // subscribe for incoming funds and notify all clients when new funds arrive
112
112
  this.incomingFundsSubscription = await this.wallet.notifyIncomingFunds(async (funds) => {
113
- if (funds.type === "vtxo" && funds.vtxos.length > 0) {
114
- // extend vtxos with taproot scripts
115
- const extendedVtxos = funds.vtxos.map((vtxo) => (0, utils_1.extendVirtualCoin)(this.wallet, vtxo));
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;
116
122
  // save vtxos using unified repository
117
- await this.walletRepository.saveVtxos(address, funds.vtxos.map((vtxo) => (0, utils_1.extendVirtualCoin)(this.wallet, vtxo)));
123
+ await this.walletRepository.saveVtxos(address, [
124
+ ...newVtxos,
125
+ ...spentVtxos,
126
+ ]);
118
127
  // notify all clients about the vtxo update
119
- this.sendMessageToAllClients("VTXO_UPDATE", JSON.stringify(extendedVtxos));
128
+ this.sendMessageToAllClients("VTXO_UPDATE", JSON.stringify({ newVtxos, spentVtxos }));
120
129
  }
121
130
  if (funds.type === "utxo" && funds.coins.length > 0) {
122
131
  // notify all clients about the utxo update
@@ -498,6 +507,10 @@ class Worker {
498
507
  await this.handleClear(event);
499
508
  break;
500
509
  }
510
+ case "RELOAD_WALLET": {
511
+ await this.handleReloadWallet(event);
512
+ break;
513
+ }
501
514
  default:
502
515
  event.source?.postMessage(response_1.Response.error(message.id, "Unknown message type"));
503
516
  }
@@ -514,5 +527,27 @@ class Worker {
514
527
  });
515
528
  });
516
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
+ }
517
552
  }
518
553
  exports.Worker = Worker;
@@ -679,7 +679,8 @@ class Wallet {
679
679
  if (update.newVtxos?.length > 0) {
680
680
  eventCallback({
681
681
  type: "vtxo",
682
- vtxos: update.newVtxos.map((vtxo) => (0, utils_1.extendVirtualCoin)(this, vtxo)),
682
+ newVtxos: update.newVtxos.map((vtxo) => (0, utils_1.extendVirtualCoin)(this, vtxo)),
683
+ spentVtxos: update.spentVtxos.map((vtxo) => (0, utils_1.extendVirtualCoin)(this, vtxo)),
683
684
  });
684
685
  }
685
686
  }
@@ -49,7 +49,7 @@ export class WalletRepositoryImpl {
49
49
  try {
50
50
  const parsed = JSON.parse(stored);
51
51
  const vtxos = parsed.map(deserializeVtxo);
52
- this.cache.vtxos.set(address, vtxos);
52
+ this.cache.vtxos.set(address, vtxos.slice());
53
53
  return vtxos.slice();
54
54
  }
55
55
  catch (error) {
@@ -67,7 +67,7 @@ export class WalletRepositoryImpl {
67
67
  else {
68
68
  vtxos.push(vtxo);
69
69
  }
70
- this.cache.vtxos.set(address, vtxos);
70
+ this.cache.vtxos.set(address, vtxos.slice());
71
71
  await this.storage.setItem(`vtxos:${address}`, JSON.stringify(vtxos.map(serializeVtxo)));
72
72
  }
73
73
  async saveVtxos(address, vtxos) {
@@ -81,14 +81,14 @@ export class WalletRepositoryImpl {
81
81
  storedVtxos.push(vtxo);
82
82
  }
83
83
  }
84
- this.cache.vtxos.set(address, storedVtxos);
84
+ this.cache.vtxos.set(address, storedVtxos.slice());
85
85
  await this.storage.setItem(`vtxos:${address}`, JSON.stringify(storedVtxos.map(serializeVtxo)));
86
86
  }
87
87
  async removeVtxo(address, vtxoId) {
88
88
  const vtxos = await this.getVtxos(address);
89
89
  const [txid, vout] = vtxoId.split(":");
90
90
  const filtered = vtxos.filter((v) => !(v.txid === txid && v.vout === parseInt(vout, 10)));
91
- this.cache.vtxos.set(address, filtered);
91
+ this.cache.vtxos.set(address, filtered.slice());
92
92
  await this.storage.setItem(`vtxos:${address}`, JSON.stringify(filtered.map(serializeVtxo)));
93
93
  }
94
94
  async clearVtxos(address) {
@@ -1,9 +1,11 @@
1
- import { Address, p2tr, TAP_LEAF_VERSION, taprootListToTree, } from "@scure/btc-signer/payment.js";
1
+ import { Script, Address, p2tr, taprootListToTree } from "@scure/btc-signer";
2
+ import { TAP_LEAF_VERSION } from "@scure/btc-signer/payment.js";
3
+ import { PSBTOutput } from "@scure/btc-signer/psbt.js";
2
4
  import { TAPROOT_UNSPENDABLE_KEY, } from "@scure/btc-signer/utils.js";
3
- import { ArkAddress } from './address.js';
4
- import { Script } from "@scure/btc-signer/script.js";
5
5
  import { hex } from "@scure/base";
6
+ import { ArkAddress } from './address.js';
6
7
  import { ConditionCSVMultisigTapscript, CSVMultisigTapscript, } from './tapscript.js';
8
+ const TapTreeCoder = PSBTOutput.tapTree[2];
7
9
  export function scriptFromTapLeafScript(leaf) {
8
10
  return leaf[1].subarray(0, leaf[1].length - 1); // remove the version byte
9
11
  }
@@ -17,8 +19,9 @@ export function scriptFromTapLeafScript(leaf) {
17
19
  */
18
20
  export class VtxoScript {
19
21
  static decode(tapTree) {
20
- const leaves = decodeTaprootTree(tapTree);
21
- return new VtxoScript(leaves);
22
+ const leaves = TapTreeCoder.decode(tapTree);
23
+ const scripts = leaves.map((leaf) => leaf.script);
24
+ return new VtxoScript(scripts);
22
25
  }
23
26
  constructor(scripts) {
24
27
  this.scripts = scripts;
@@ -32,7 +35,11 @@ export class VtxoScript {
32
35
  this.tweakedPublicKey = payment.tweakedPubkey;
33
36
  }
34
37
  encode() {
35
- const tapTree = encodeTaprootTree(this.scripts);
38
+ const tapTree = TapTreeCoder.encode(this.scripts.map((script) => ({
39
+ depth: 1,
40
+ version: TAP_LEAF_VERSION,
41
+ script,
42
+ })));
36
43
  return tapTree;
37
44
  }
38
45
  address(prefix, serverPubKey) {
@@ -75,89 +82,3 @@ export class VtxoScript {
75
82
  return paths;
76
83
  }
77
84
  }
78
- function decodeTaprootTree(tapTree) {
79
- let offset = 0;
80
- const scripts = [];
81
- // Read number of leaves
82
- const [numLeaves, numLeavesSize] = decodeCompactSizeUint(tapTree, offset);
83
- offset += numLeavesSize;
84
- // Read each leaf
85
- for (let i = 0; i < numLeaves; i++) {
86
- // Skip depth (1 byte)
87
- offset += 1;
88
- // Skip leaf version (1 byte)
89
- offset += 1;
90
- // Read script length
91
- const [scriptLength, scriptLengthSize] = decodeCompactSizeUint(tapTree, offset);
92
- offset += scriptLengthSize;
93
- // Read script content
94
- const script = tapTree.slice(offset, offset + scriptLength);
95
- scripts.push(script);
96
- offset += scriptLength;
97
- }
98
- return scripts;
99
- }
100
- function decodeCompactSizeUint(data, offset) {
101
- const firstByte = data[offset];
102
- if (firstByte < 0xfd) {
103
- return [firstByte, 1];
104
- }
105
- else if (firstByte === 0xfd) {
106
- const value = new DataView(data.buffer).getUint16(offset + 1, true);
107
- return [value, 3];
108
- }
109
- else if (firstByte === 0xfe) {
110
- const value = new DataView(data.buffer).getUint32(offset + 1, true);
111
- return [value, 5];
112
- }
113
- else {
114
- const value = Number(new DataView(data.buffer).getBigUint64(offset + 1, true));
115
- return [value, 9];
116
- }
117
- }
118
- function encodeTaprootTree(leaves) {
119
- const chunks = [];
120
- // Write number of leaves as compact size uint
121
- chunks.push(encodeCompactSizeUint(leaves.length));
122
- for (const tapscript of leaves) {
123
- // Write depth (always 1 for now)
124
- chunks.push(new Uint8Array([1]));
125
- // Write leaf version (0xc0 for tapscript)
126
- chunks.push(new Uint8Array([0xc0]));
127
- // Write script length and script
128
- chunks.push(encodeCompactSizeUint(tapscript.length));
129
- chunks.push(tapscript);
130
- }
131
- // Concatenate all chunks
132
- const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
133
- const result = new Uint8Array(totalLength);
134
- let offset = 0;
135
- for (const chunk of chunks) {
136
- result.set(chunk, offset);
137
- offset += chunk.length;
138
- }
139
- return result;
140
- }
141
- function encodeCompactSizeUint(value) {
142
- if (value < 0xfd) {
143
- return new Uint8Array([value]);
144
- }
145
- else if (value <= 0xffff) {
146
- const buffer = new Uint8Array(3);
147
- buffer[0] = 0xfd;
148
- new DataView(buffer.buffer).setUint16(1, value, true);
149
- return buffer;
150
- }
151
- else if (value <= 0xffffffff) {
152
- const buffer = new Uint8Array(5);
153
- buffer[0] = 0xfe;
154
- new DataView(buffer.buffer).setUint32(1, value, true);
155
- return buffer;
156
- }
157
- else {
158
- const buffer = new Uint8Array(9);
159
- buffer[0] = 0xff;
160
- new DataView(buffer.buffer).setBigUint64(1, BigInt(value), true);
161
- return buffer;
162
- }
163
- }
@@ -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 = {}));
@@ -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));
@@ -107,13 +107,22 @@ export class Worker {
107
107
  this.incomingFundsSubscription();
108
108
  // subscribe for incoming funds and notify all clients when new funds arrive
109
109
  this.incomingFundsSubscription = await this.wallet.notifyIncomingFunds(async (funds) => {
110
- if (funds.type === "vtxo" && funds.vtxos.length > 0) {
111
- // extend vtxos with taproot scripts
112
- const extendedVtxos = funds.vtxos.map((vtxo) => extendVirtualCoin(this.wallet, vtxo));
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;
113
119
  // save vtxos using unified repository
114
- await this.walletRepository.saveVtxos(address, funds.vtxos.map((vtxo) => extendVirtualCoin(this.wallet, vtxo)));
120
+ await this.walletRepository.saveVtxos(address, [
121
+ ...newVtxos,
122
+ ...spentVtxos,
123
+ ]);
115
124
  // notify all clients about the vtxo update
116
- this.sendMessageToAllClients("VTXO_UPDATE", JSON.stringify(extendedVtxos));
125
+ this.sendMessageToAllClients("VTXO_UPDATE", JSON.stringify({ newVtxos, spentVtxos }));
117
126
  }
118
127
  if (funds.type === "utxo" && funds.coins.length > 0) {
119
128
  // notify all clients about the utxo update
@@ -495,6 +504,10 @@ export class Worker {
495
504
  await this.handleClear(event);
496
505
  break;
497
506
  }
507
+ case "RELOAD_WALLET": {
508
+ await this.handleReloadWallet(event);
509
+ break;
510
+ }
498
511
  default:
499
512
  event.source?.postMessage(Response.error(message.id, "Unknown message type"));
500
513
  }
@@ -511,4 +524,26 @@ export class Worker {
511
524
  });
512
525
  });
513
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
+ }
514
549
  }
@@ -642,7 +642,8 @@ export class Wallet {
642
642
  if (update.newVtxos?.length > 0) {
643
643
  eventCallback({
644
644
  type: "vtxo",
645
- vtxos: update.newVtxos.map((vtxo) => extendVirtualCoin(this, vtxo)),
645
+ newVtxos: update.newVtxos.map((vtxo) => extendVirtualCoin(this, vtxo)),
646
+ spentVtxos: update.spentVtxos.map((vtxo) => extendVirtualCoin(this, vtxo)),
646
647
  });
647
648
  }
648
649
  }
@@ -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
  }
@@ -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 {};
@@ -40,4 +40,5 @@ export declare class Worker {
40
40
  private handleGetStatus;
41
41
  private handleMessage;
42
42
  private sendMessageToAllClients;
43
+ private handleReloadWallet;
43
44
  }
@@ -15,7 +15,8 @@ export type IncomingFunds = {
15
15
  coins: Coin[];
16
16
  } | {
17
17
  type: "vtxo";
18
- vtxos: ExtendedVirtualCoin[];
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.5",
3
+ "version": "0.3.0-alpha.7",
4
4
  "description": "Bitcoin wallet SDK with Taproot and Ark integration",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",