@1sat/wallet-toolbox 0.0.8 → 0.0.9

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 (55) hide show
  1. package/dist/api/OneSatApi.d.ts +100 -0
  2. package/dist/api/OneSatApi.js +156 -0
  3. package/dist/api/balance/index.d.ts +38 -0
  4. package/dist/api/balance/index.js +82 -0
  5. package/dist/api/broadcast/index.d.ts +22 -0
  6. package/dist/api/broadcast/index.js +45 -0
  7. package/dist/api/constants.d.ts +21 -0
  8. package/dist/api/constants.js +29 -0
  9. package/dist/api/index.d.ts +36 -0
  10. package/dist/api/index.js +38 -0
  11. package/dist/api/inscriptions/index.d.ts +25 -0
  12. package/dist/api/inscriptions/index.js +50 -0
  13. package/dist/api/locks/index.d.ts +44 -0
  14. package/dist/api/locks/index.js +233 -0
  15. package/dist/api/ordinals/index.d.ts +87 -0
  16. package/dist/api/ordinals/index.js +446 -0
  17. package/dist/api/payments/index.d.ts +37 -0
  18. package/dist/api/payments/index.js +130 -0
  19. package/dist/api/signing/index.d.ts +32 -0
  20. package/dist/api/signing/index.js +41 -0
  21. package/dist/api/tokens/index.d.ts +88 -0
  22. package/dist/api/tokens/index.js +400 -0
  23. package/dist/cwi/chrome.d.ts +11 -0
  24. package/dist/cwi/chrome.js +39 -0
  25. package/dist/cwi/event.d.ts +11 -0
  26. package/dist/cwi/event.js +38 -0
  27. package/dist/cwi/factory.d.ts +14 -0
  28. package/dist/cwi/factory.js +44 -0
  29. package/dist/cwi/index.d.ts +11 -0
  30. package/dist/cwi/index.js +11 -0
  31. package/dist/cwi/types.d.ts +39 -0
  32. package/dist/cwi/types.js +39 -0
  33. package/dist/index.d.ts +2 -1
  34. package/dist/index.js +5 -1
  35. package/dist/indexers/CosignIndexer.js +1 -0
  36. package/dist/indexers/InscriptionIndexer.js +3 -4
  37. package/dist/indexers/LockIndexer.js +1 -0
  38. package/dist/indexers/OrdLockIndexer.js +1 -0
  39. package/dist/indexers/OriginIndexer.js +1 -1
  40. package/dist/indexers/index.d.ts +1 -1
  41. package/dist/indexers/types.d.ts +18 -0
  42. package/dist/services/OneSatServices.d.ts +19 -10
  43. package/dist/services/OneSatServices.js +201 -39
  44. package/dist/services/client/ChaintracksClient.d.ts +55 -13
  45. package/dist/services/client/ChaintracksClient.js +123 -28
  46. package/dist/services/client/OrdfsClient.d.ts +2 -2
  47. package/dist/services/client/OrdfsClient.js +4 -3
  48. package/dist/services/client/TxoClient.js +9 -0
  49. package/dist/sync/AddressManager.d.ts +85 -0
  50. package/dist/sync/AddressManager.js +107 -0
  51. package/dist/sync/SyncManager.d.ts +207 -0
  52. package/dist/sync/SyncManager.js +507 -0
  53. package/dist/sync/index.d.ts +4 -0
  54. package/dist/sync/index.js +2 -0
  55. package/package.json +5 -4
@@ -0,0 +1,507 @@
1
+ /**
2
+ * SyncManager - Syncs external transactions to the BRC-100 wallet.
3
+ *
4
+ * Split into two components for Chrome extension compatibility:
5
+ * - SyncFetcher: Calls SSE endpoint and enqueues txids (runs in UI context)
6
+ * - SyncProcessor: Processes queue and internalizes outputs (runs in service worker)
7
+ *
8
+ * This separation is necessary because EventSource (SSE) doesn't work reliably
9
+ * in Chrome extension service workers due to their ephemeral nature.
10
+ */
11
+ import { Beef } from "@bsv/sdk";
12
+ import { Outpoint } from "../indexers/Outpoint";
13
+ import { FundIndexer } from "../indexers/FundIndexer";
14
+ import { InscriptionIndexer } from "../indexers/InscriptionIndexer";
15
+ import { Bsv21Indexer } from "../indexers/Bsv21Indexer";
16
+ import { OriginIndexer } from "../indexers/OriginIndexer";
17
+ import { OpNSIndexer } from "../indexers/OpNSIndexer";
18
+ import { SigmaIndexer } from "../indexers/SigmaIndexer";
19
+ import { MapIndexer } from "../indexers/MapIndexer";
20
+ /** Reorg-safe depth - only update lastQueuedScore for outputs this many blocks deep */
21
+ const REORG_SAFE_DEPTH = 6;
22
+ /** Default batch size for queue processing */
23
+ const DEFAULT_BATCH_SIZE = 20;
24
+ /**
25
+ * SyncFetcher - Fetches outputs via SSE and enqueues them.
26
+ *
27
+ * Run this from the UI context (popup) when the wallet opens.
28
+ * It will fetch all new outputs since the last sync and add them to the queue.
29
+ */
30
+ export class SyncFetcher {
31
+ services;
32
+ syncQueue;
33
+ addressManager;
34
+ unsubscribeStream = null;
35
+ listeners = new Map();
36
+ constructor(options) {
37
+ this.services = options.services;
38
+ this.syncQueue = options.syncQueue;
39
+ this.addressManager = options.addressManager;
40
+ }
41
+ on(event, listener) {
42
+ if (!this.listeners.has(event)) {
43
+ this.listeners.set(event, new Set());
44
+ }
45
+ this.listeners.get(event).add(listener);
46
+ }
47
+ off(event, listener) {
48
+ this.listeners.get(event)?.delete(listener);
49
+ }
50
+ emit(event, data) {
51
+ this.listeners.get(event)?.forEach((listener) => listener(data));
52
+ }
53
+ /**
54
+ * Fetch new outputs via SSE and enqueue them.
55
+ * Returns when the stream completes (all historical data fetched).
56
+ */
57
+ async fetch(currentHeight) {
58
+ if (this.unsubscribeStream) {
59
+ console.warn("Fetch already running");
60
+ return 0;
61
+ }
62
+ const addresses = this.addressManager.getAddresses();
63
+ if (addresses.length === 0) {
64
+ console.warn("No addresses to fetch");
65
+ return 0;
66
+ }
67
+ this.emit("fetch:start", { addresses });
68
+ // Get last score for resumption
69
+ const state = await this.syncQueue.getState();
70
+ const fromScore = state.lastQueuedScore;
71
+ let queuedCount = 0;
72
+ return new Promise((resolve, reject) => {
73
+ this.unsubscribeStream = this.services.owner.sync(addresses, async (output) => {
74
+ await this.handleSyncOutput(output, currentHeight);
75
+ queuedCount++;
76
+ this.emit("fetch:queued", { outpoint: output.outpoint, score: output.score });
77
+ }, fromScore, () => {
78
+ // Stream complete
79
+ this.unsubscribeStream = null;
80
+ this.emit("fetch:complete", { queuedCount });
81
+ resolve(queuedCount);
82
+ }, (error) => {
83
+ this.unsubscribeStream = null;
84
+ this.emit("fetch:error", { message: error.message });
85
+ reject(error);
86
+ });
87
+ });
88
+ }
89
+ /**
90
+ * Stop the SSE stream if running.
91
+ */
92
+ stop() {
93
+ if (this.unsubscribeStream) {
94
+ this.unsubscribeStream();
95
+ this.unsubscribeStream = null;
96
+ }
97
+ }
98
+ async handleSyncOutput(output, currentHeight) {
99
+ // Enqueue the output
100
+ await this.syncQueue.enqueue([
101
+ {
102
+ outpoint: output.outpoint,
103
+ score: output.score,
104
+ spendTxid: output.spendTxid,
105
+ },
106
+ ]);
107
+ // Update lastQueuedScore only for reorg-safe outputs
108
+ const outputHeight = Math.floor(output.score / 1e9);
109
+ if (currentHeight - outputHeight >= REORG_SAFE_DEPTH) {
110
+ await this.syncQueue.setState({ lastQueuedScore: output.score });
111
+ }
112
+ }
113
+ }
114
+ /**
115
+ * SyncProcessor - Processes the sync queue and internalizes outputs.
116
+ *
117
+ * Run this from the service worker. It will process items from the queue
118
+ * until the queue is empty or stop() is called.
119
+ */
120
+ export class SyncProcessor {
121
+ wallet;
122
+ services;
123
+ syncQueue;
124
+ addressManager;
125
+ network;
126
+ batchSize;
127
+ indexers;
128
+ processorRunning = false;
129
+ stopRequested = false;
130
+ listeners = new Map();
131
+ constructor(options) {
132
+ this.wallet = options.wallet;
133
+ this.services = options.services;
134
+ this.syncQueue = options.syncQueue;
135
+ this.addressManager = options.addressManager;
136
+ this.network = options.network;
137
+ this.batchSize = options.batchSize ?? DEFAULT_BATCH_SIZE;
138
+ // Build owners set from address manager
139
+ const owners = new Set(this.addressManager.getAddresses());
140
+ // Initialize indexers for sync (P2PKH-based outputs only)
141
+ this.indexers = options.indexers ?? [
142
+ new FundIndexer(owners, this.network),
143
+ new InscriptionIndexer(owners, this.network),
144
+ new Bsv21Indexer(owners, this.network, this.services),
145
+ new OriginIndexer(owners, this.network, this.services),
146
+ new OpNSIndexer(owners, this.network),
147
+ new SigmaIndexer(owners, this.network),
148
+ new MapIndexer(owners, this.network),
149
+ ];
150
+ }
151
+ on(event, listener) {
152
+ if (!this.listeners.has(event)) {
153
+ this.listeners.set(event, new Set());
154
+ }
155
+ this.listeners.get(event).add(listener);
156
+ }
157
+ off(event, listener) {
158
+ this.listeners.get(event)?.delete(listener);
159
+ }
160
+ emit(event, data) {
161
+ this.listeners.get(event)?.forEach((listener) => listener(data));
162
+ }
163
+ /**
164
+ * Start processing the queue.
165
+ * Processes until queue is empty or stop() is called.
166
+ */
167
+ async start() {
168
+ if (this.processorRunning) {
169
+ console.warn("Processor already running");
170
+ return;
171
+ }
172
+ // Reset any stuck processing items
173
+ await this.syncQueue.resetProcessing();
174
+ this.stopRequested = false;
175
+ this.emit("process:start", {});
176
+ await this.processQueueLoop();
177
+ }
178
+ /**
179
+ * Stop processing the queue.
180
+ */
181
+ stop() {
182
+ this.stopRequested = true;
183
+ }
184
+ /**
185
+ * Check if processor is currently running.
186
+ */
187
+ isRunning() {
188
+ return this.processorRunning;
189
+ }
190
+ async processQueueLoop() {
191
+ this.processorRunning = true;
192
+ try {
193
+ while (!this.stopRequested) {
194
+ // Claim batch of pending items grouped by txid
195
+ const byTxid = await this.syncQueue.claim(this.batchSize);
196
+ if (byTxid.size === 0) {
197
+ // Queue empty - wait and poll again
198
+ await new Promise((resolve) => setTimeout(resolve, 1000));
199
+ continue;
200
+ }
201
+ // Process each txid in parallel
202
+ await Promise.all(Array.from(byTxid.entries()).map(([txid, items]) => this.processTxid(txid, items)));
203
+ // Emit progress
204
+ const stats = await this.syncQueue.getStats();
205
+ this.emit("process:progress", {
206
+ pending: stats.pending,
207
+ done: stats.done,
208
+ failed: stats.failed,
209
+ });
210
+ }
211
+ this.emit("process:complete", {});
212
+ }
213
+ catch (error) {
214
+ this.emit("process:error", {
215
+ message: error instanceof Error ? error.message : String(error),
216
+ });
217
+ }
218
+ finally {
219
+ this.processorRunning = false;
220
+ }
221
+ }
222
+ async processTxid(txid, items) {
223
+ try {
224
+ // Build spend map: vout → spendTxid
225
+ const spendMap = new Map();
226
+ for (const item of items) {
227
+ if (item.spendTxid) {
228
+ const vout = parseInt(item.outpoint.split("_")[1], 10);
229
+ spendMap.set(vout, item.spendTxid);
230
+ }
231
+ }
232
+ // If all items are spend-only (no new outputs), just mark outputs as spent
233
+ const hasNewOutputs = items.some((item) => !item.spendTxid);
234
+ if (!hasNewOutputs) {
235
+ // TODO: Mark outputs as spent in wallet storage
236
+ // For now, just complete the items
237
+ await this.syncQueue.completeMany(items.map((i) => i.id));
238
+ return;
239
+ }
240
+ // Load transaction as BEEF
241
+ const beef = await this.services.beef.getBeef(txid);
242
+ if (!beef) {
243
+ throw new Error(`Failed to load transaction ${txid}`);
244
+ }
245
+ // Parse transaction with indexers
246
+ const beefObj = Beef.fromBinary(Array.from(beef));
247
+ const btx = beefObj.findTxid(txid);
248
+ if (!btx?.tx) {
249
+ throw new Error(`Transaction ${txid} not found in BEEF`);
250
+ }
251
+ const ctx = await this.parseTransaction(btx.tx);
252
+ // Build InternalizeOutput entries for owned outputs
253
+ const outputs = [];
254
+ let internalizedCount = 0;
255
+ for (const txo of ctx.txos) {
256
+ if (!txo.owner)
257
+ continue;
258
+ // Check if this output is at one of our receive addresses
259
+ const derivation = this.addressManager.getDerivation(txo.owner);
260
+ if (!derivation)
261
+ continue;
262
+ // Build internalize output using protocol from indexer
263
+ const internalizeOutput = this.buildInternalizeOutput(txo, derivation);
264
+ if (internalizeOutput) {
265
+ outputs.push(internalizeOutput);
266
+ internalizedCount++;
267
+ }
268
+ }
269
+ // Internalize if we have owned outputs
270
+ if (outputs.length > 0) {
271
+ const args = {
272
+ tx: Array.from(beef),
273
+ outputs,
274
+ description: "1sat sync",
275
+ };
276
+ console.log(`[SyncProcessor] Internalizing ${outputs.length} outputs for txid ${txid}`, outputs.map((o) => ({ vout: o.outputIndex, protocol: o.protocol })));
277
+ await this.wallet.internalizeAction(args);
278
+ console.log(`[SyncProcessor] Internalization complete for txid ${txid}`);
279
+ this.emit("process:parsed", { internalizedCount });
280
+ }
281
+ // Complete all items for this txid
282
+ await this.syncQueue.completeMany(items.map((i) => i.id));
283
+ }
284
+ catch (error) {
285
+ // Fail all items for this txid
286
+ const errorMsg = error instanceof Error ? error.message : String(error);
287
+ console.error(`[SyncProcessor] Failed to process txid ${txid}:`, errorMsg);
288
+ for (const item of items) {
289
+ await this.syncQueue.fail(item.id, errorMsg);
290
+ }
291
+ }
292
+ }
293
+ async parseTransaction(tx) {
294
+ const txid = tx.id("hex");
295
+ // Initialize parse context
296
+ const ctx = {
297
+ tx,
298
+ txid,
299
+ txos: [],
300
+ spends: [],
301
+ summary: {},
302
+ indexers: this.indexers,
303
+ };
304
+ // Parse each output
305
+ for (let vout = 0; vout < tx.outputs.length; vout++) {
306
+ const output = tx.outputs[vout];
307
+ const txo = {
308
+ output,
309
+ outpoint: new Outpoint(txid, vout),
310
+ data: {},
311
+ };
312
+ // Run through all indexers
313
+ for (const indexer of this.indexers) {
314
+ const result = await indexer.parse(txo);
315
+ if (result) {
316
+ txo.data[indexer.tag] = {
317
+ data: result.data,
318
+ tags: result.tags,
319
+ content: result.content,
320
+ };
321
+ // First indexer to set owner wins
322
+ if (result.owner && !txo.owner) {
323
+ txo.owner = result.owner;
324
+ }
325
+ // First indexer to set basket wins
326
+ if (result.basket && !txo.basket) {
327
+ txo.basket = result.basket;
328
+ }
329
+ // First indexer to set protocol wins (custom scripts override P2PKH)
330
+ if (result.protocol && !txo.protocol) {
331
+ txo.protocol = result.protocol;
332
+ }
333
+ }
334
+ }
335
+ ctx.txos.push(txo);
336
+ }
337
+ // Run summarize phase for all indexers
338
+ for (const indexer of this.indexers) {
339
+ const summary = await indexer.summarize(ctx, true);
340
+ if (summary) {
341
+ ctx.summary[indexer.tag] = summary;
342
+ }
343
+ }
344
+ return ctx;
345
+ }
346
+ buildInternalizeOutput(txo, derivation) {
347
+ const vout = txo.outpoint.vout;
348
+ // Use protocol from indexer, default to "wallet payment" for P2PKH
349
+ const protocol = txo.protocol || "wallet payment";
350
+ if (protocol === "basket insertion") {
351
+ // Custom script - use basket insertion
352
+ // These outputs need custom unlock scripts when spent
353
+ const basket = txo.basket || "custom";
354
+ const tags = this.collectTags(txo);
355
+ return {
356
+ outputIndex: vout,
357
+ protocol: "basket insertion",
358
+ insertionRemittance: {
359
+ basket,
360
+ tags,
361
+ // Store derivation info for future signing
362
+ customInstructions: JSON.stringify({
363
+ derivationPrefix: derivation.derivationPrefix,
364
+ derivationSuffix: derivation.derivationSuffix,
365
+ senderIdentityKey: derivation.senderIdentityKey,
366
+ }),
367
+ },
368
+ };
369
+ }
370
+ else {
371
+ // P2PKH-based output - use wallet payment for auto-signing
372
+ return {
373
+ outputIndex: vout,
374
+ protocol: "wallet payment",
375
+ paymentRemittance: {
376
+ derivationPrefix: derivation.derivationPrefix,
377
+ derivationSuffix: derivation.derivationSuffix,
378
+ senderIdentityKey: derivation.senderIdentityKey,
379
+ },
380
+ };
381
+ }
382
+ }
383
+ collectTags(txo) {
384
+ const tags = [];
385
+ for (const indexData of Object.values(txo.data)) {
386
+ tags.push(...indexData.tags);
387
+ }
388
+ return tags;
389
+ }
390
+ }
391
+ /**
392
+ * SyncManager - Legacy class that combines SSE fetching and queue processing.
393
+ *
394
+ * @deprecated For Chrome extensions, use SyncFetcher (in UI) and SyncProcessor (in service worker)
395
+ * separately since EventSource doesn't work reliably in service workers.
396
+ *
397
+ * This class is kept for backwards compatibility and for environments where
398
+ * SSE works (e.g., Node.js, React Native).
399
+ */
400
+ export class SyncManager {
401
+ fetcher;
402
+ processor;
403
+ wallet;
404
+ addressManager;
405
+ listeners = new Map();
406
+ fetchPromise = null;
407
+ constructor(options) {
408
+ this.wallet = options.wallet;
409
+ this.addressManager = options.addressManager;
410
+ // Create fetcher for SSE stream
411
+ this.fetcher = new SyncFetcher({
412
+ services: options.services,
413
+ syncQueue: options.syncQueue,
414
+ addressManager: options.addressManager,
415
+ });
416
+ // Create processor for queue processing
417
+ this.processor = new SyncProcessor({
418
+ wallet: options.wallet,
419
+ services: options.services,
420
+ syncQueue: options.syncQueue,
421
+ addressManager: options.addressManager,
422
+ network: options.network,
423
+ batchSize: options.batchSize,
424
+ indexers: options.indexers,
425
+ });
426
+ // Wire up event forwarding
427
+ this.setupEventForwarding();
428
+ }
429
+ setupEventForwarding() {
430
+ // Forward fetcher events
431
+ this.fetcher.on("fetch:start", (data) => {
432
+ this.emit("sync:start", { addresses: data.addresses });
433
+ });
434
+ this.fetcher.on("fetch:error", (data) => {
435
+ this.emit("sync:error", { message: data.message });
436
+ });
437
+ // Forward processor events
438
+ this.processor.on("process:progress", (data) => {
439
+ this.emit("sync:progress", data);
440
+ });
441
+ this.processor.on("process:complete", () => {
442
+ this.emit("sync:complete", {});
443
+ });
444
+ this.processor.on("process:error", (data) => {
445
+ this.emit("sync:error", { message: data.message });
446
+ });
447
+ this.processor.on("process:parsed", (data) => {
448
+ this.emit("sync:parsed", data);
449
+ });
450
+ }
451
+ // ===== Event Emitter =====
452
+ on(event, listener) {
453
+ if (!this.listeners.has(event)) {
454
+ this.listeners.set(event, new Set());
455
+ }
456
+ this.listeners.get(event).add(listener);
457
+ }
458
+ off(event, listener) {
459
+ this.listeners.get(event)?.delete(listener);
460
+ }
461
+ emit(event, data) {
462
+ this.listeners.get(event)?.forEach((listener) => listener(data));
463
+ }
464
+ // ===== Sync Control =====
465
+ /**
466
+ * Start syncing - opens SSE stream and starts queue processor.
467
+ */
468
+ async sync() {
469
+ if (this.fetchPromise || this.processor.isRunning()) {
470
+ console.warn("Sync already running");
471
+ return;
472
+ }
473
+ const addresses = this.addressManager.getAddresses();
474
+ if (addresses.length === 0) {
475
+ console.warn("No addresses to sync");
476
+ return;
477
+ }
478
+ // Get current height for reorg protection
479
+ const { height: currentHeight } = await this.wallet.getHeight({});
480
+ // Start fetcher (SSE stream) - this runs until stream completes
481
+ this.fetchPromise = this.fetcher.fetch(currentHeight);
482
+ // Start processor in parallel - this processes items as they come in
483
+ // and waits for more until fetch completes
484
+ this.processor.start();
485
+ // Wait for fetch to complete, then let processor finish
486
+ try {
487
+ await this.fetchPromise;
488
+ }
489
+ finally {
490
+ this.fetchPromise = null;
491
+ }
492
+ }
493
+ /**
494
+ * Stop syncing - closes SSE stream and stops queue processor.
495
+ */
496
+ stop() {
497
+ this.fetcher.stop();
498
+ this.processor.stop();
499
+ this.fetchPromise = null;
500
+ }
501
+ /**
502
+ * Check if sync is currently running.
503
+ */
504
+ isSyncing() {
505
+ return this.fetchPromise !== null || this.processor.isRunning();
506
+ }
507
+ }
@@ -1,3 +1,7 @@
1
1
  export * from "./types";
2
2
  export { IndexedDbSyncQueue } from "./IndexedDbSyncQueue";
3
3
  export { SqliteSyncQueue } from "./SqliteSyncQueue";
4
+ export { AddressManager, YOURS_PREFIX, BRC29_PROTOCOL_ID } from "./AddressManager";
5
+ export type { AddressDerivation } from "./AddressManager";
6
+ export { SyncFetcher, SyncProcessor, SyncManager } from "./SyncManager";
7
+ export type { SyncFetcherOptions, SyncFetcherEvents, SyncProcessorOptions, SyncProcessorEvents, SyncManagerOptions, SyncEvents, } from "./SyncManager";
@@ -1,3 +1,5 @@
1
1
  export * from "./types";
2
2
  export { IndexedDbSyncQueue } from "./IndexedDbSyncQueue";
3
3
  export { SqliteSyncQueue } from "./SqliteSyncQueue";
4
+ export { AddressManager, YOURS_PREFIX, BRC29_PROTOCOL_ID } from "./AddressManager";
5
+ export { SyncFetcher, SyncProcessor, SyncManager } from "./SyncManager";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1sat/wallet-toolbox",
3
- "version": "0.0.8",
3
+ "version": "0.0.9",
4
4
  "description": "BSV wallet library extending @bsv/wallet-toolbox with 1Sat Ordinals protocol support",
5
5
  "author": "1Sat Team",
6
6
  "license": "MIT",
@@ -37,14 +37,15 @@
37
37
  "tester": "bun run build && bun run tester:build && bun ./tester/server.ts"
38
38
  },
39
39
  "dependencies": {
40
- "@bopen-io/templates": "^1.1.2",
41
- "@bsv/sdk": "^1.9.29",
40
+ "@bopen-io/templates": "^1.1.4",
41
+ "@bsv/sdk": "^1.9.31",
42
42
  "buffer": "^6.0.3"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@biomejs/biome": "^1.9.4",
46
- "@bsv/wallet-toolbox": "^1.7.18",
46
+ "@bsv/wallet-toolbox": "npm:@bopen-io/wallet-toolbox@^1.7.19",
47
47
  "@types/bun": "^1.3.4",
48
+ "@types/chrome": "^0.1.32",
48
49
  "typescript": "^5.9.3"
49
50
  }
50
51
  }