@1sat/wallet-toolbox 0.0.74 → 0.0.75

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.
@@ -107,8 +107,4 @@ export const getExchangeRate = {
107
107
  // Module exports
108
108
  // ============================================================================
109
109
  /** All balance skills for registry */
110
- export const balanceSkills = [
111
- getBalance,
112
- getPaymentUtxos,
113
- getExchangeRate,
114
- ];
110
+ export const balanceSkills = [getBalance, getPaymentUtxos, getExchangeRate];
@@ -228,6 +228,9 @@ export const unlockBsv = {
228
228
  for (let i = 0; i < maturedLocks.length; i++) {
229
229
  const lock = maturedLocks[i];
230
230
  const input = tx.inputs[i];
231
+ if (!lock.output.lockingScript || !input.sourceTXID) {
232
+ return { error: "missing-lock-data" };
233
+ }
231
234
  const lockingScript = Script.fromHex(lock.output.lockingScript);
232
235
  const preimage = TransactionSignature.format({
233
236
  sourceTXID: input.sourceTXID,
@@ -175,9 +175,12 @@ export async function buildTransferOrdinals(ctx, request) {
175
175
  });
176
176
  recipientAddress = PublicKey.fromString(publicKey).toAddress();
177
177
  }
178
- else {
178
+ else if (address) {
179
179
  recipientAddress = address;
180
180
  }
181
+ else {
182
+ return { error: "must-provide-counterparty-or-address" };
183
+ }
181
184
  // Preserve important tags from source output
182
185
  const tags = [];
183
186
  for (const tag of ordinal.tags ?? []) {
@@ -188,7 +191,7 @@ export async function buildTransferOrdinals(ctx, request) {
188
191
  }
189
192
  }
190
193
  const sourceName = extractName(ordinal.customInstructions);
191
- inputs.push({
194
+ inputs?.push({
192
195
  outpoint,
193
196
  inputDescription: "Ordinal to transfer",
194
197
  unlockingScriptLength: 108,
@@ -196,7 +199,7 @@ export async function buildTransferOrdinals(ctx, request) {
196
199
  // Only track output in wallet when transferring to a counterparty (wallet can derive keys to spend it)
197
200
  // External address transfers are NOT tracked since the wallet cannot spend them
198
201
  if (counterparty) {
199
- outputs.push({
202
+ outputs?.push({
200
203
  lockingScript: new P2PKH().lock(recipientAddress).toHex(),
201
204
  satoshis: 1,
202
205
  outputDescription: "Ordinal transfer",
@@ -211,7 +214,7 @@ export async function buildTransferOrdinals(ctx, request) {
211
214
  }
212
215
  else {
213
216
  // External address - output is not tracked in wallet
214
- outputs.push({
217
+ outputs?.push({
215
218
  lockingScript: new P2PKH().lock(recipientAddress).toHex(),
216
219
  satoshis: 1,
217
220
  outputDescription: "Ordinal transfer to external address",
@@ -396,7 +399,7 @@ export const transferOrdinals = {
396
399
  inputs: params.inputs,
397
400
  outputs: params.outputs?.map((o) => ({
398
401
  ...o,
399
- lockingScript: o.lockingScript?.slice(0, 20) + "...",
402
+ lockingScript: `${o.lockingScript?.slice(0, 20)}...`,
400
403
  })),
401
404
  }, null, 2));
402
405
  // Debug: Check if BEEF contains the source transactions
@@ -5,8 +5,7 @@
5
5
  */
6
6
  import { BSV21 } from "@bopen-io/templates";
7
7
  import { P2PKH, PrivateKey, PublicKey, Transaction, Utils, } from "@bsv/sdk";
8
- import { deriveFundAddress } from "../../indexers";
9
- import { BSV21_BASKET, BSV21_FEE_SATS, BSV21_PROTOCOL, ONESAT_PROTOCOL, } from "../constants";
8
+ import { BSV21_BASKET, BSV21_PROTOCOL, ONESAT_PROTOCOL } from "../constants";
10
9
  export * from "./types";
11
10
  /**
12
11
  * Prepare sweep inputs from IndexedOutput objects by fetching locking scripts.
@@ -359,19 +358,19 @@ export const sweepOrdinals = {
359
358
  outputs,
360
359
  options: { signAndProcess: false, randomizeOutputs: false },
361
360
  };
362
- console.log(`[sweepOrdinals] === CREATE ACTION ARGS ===`);
361
+ console.log("[sweepOrdinals] === CREATE ACTION ARGS ===");
363
362
  console.log(`[sweepOrdinals] description: ${createActionArgs.description}`);
364
363
  console.log(`[sweepOrdinals] inputBEEF length: ${beefData.length} bytes`);
365
364
  console.log(`[sweepOrdinals] inputs count: ${inputDescriptors.length}`);
366
365
  console.log(`[sweepOrdinals] outputs count: ${outputs.length}`);
367
- console.log(`[sweepOrdinals] inputs:`, JSON.stringify(inputDescriptors, null, 2));
368
- console.log(`[sweepOrdinals] outputs:`, JSON.stringify(outputs, null, 2));
369
- console.log(`[sweepOrdinals] options:`, JSON.stringify(createActionArgs.options));
370
- console.log(`[sweepOrdinals] Calling createAction...`);
366
+ console.log("[sweepOrdinals] inputs:", JSON.stringify(inputDescriptors, null, 2));
367
+ console.log("[sweepOrdinals] outputs:", JSON.stringify(outputs, null, 2));
368
+ console.log("[sweepOrdinals] options:", JSON.stringify(createActionArgs.options));
369
+ console.log("[sweepOrdinals] Calling createAction...");
371
370
  let createResult;
372
371
  try {
373
372
  createResult = await ctx.wallet.createAction(createActionArgs);
374
- console.log(`[sweepOrdinals] createAction returned:`, JSON.stringify(createResult, (key, value) => {
373
+ console.log("[sweepOrdinals] createAction returned:", JSON.stringify(createResult, (key, value) => {
375
374
  // Don't stringify large binary data
376
375
  if (key === "tx" && value instanceof Uint8Array)
377
376
  return `<Uint8Array ${value.length} bytes>`;
@@ -381,12 +380,12 @@ export const sweepOrdinals = {
381
380
  }, 2));
382
381
  }
383
382
  catch (createError) {
384
- console.error(`[sweepOrdinals] createAction threw:`, createError);
383
+ console.error("[sweepOrdinals] createAction threw:", createError);
385
384
  const errorMsg = createError instanceof Error
386
385
  ? createError.message
387
386
  : String(createError);
388
387
  const errorStack = createError instanceof Error ? createError.stack : undefined;
389
- console.error(`[sweepOrdinals] Stack:`, errorStack);
388
+ console.error("[sweepOrdinals] Stack:", errorStack);
390
389
  return { error: `createAction failed: ${errorMsg}` };
391
390
  }
392
391
  if ("error" in createResult && createResult.error) {
@@ -398,7 +397,7 @@ export const sweepOrdinals = {
398
397
  // Sign each input with our external key
399
398
  const tx = Transaction.fromBEEF(createResult.signableTransaction.tx);
400
399
  // Log transaction structure for debugging
401
- console.log(`[sweepOrdinals] === Transaction Structure ===`);
400
+ console.log("[sweepOrdinals] === Transaction Structure ===");
402
401
  console.log(`[sweepOrdinals] Inputs (${tx.inputs.length}):`);
403
402
  let totalInputSats = 0;
404
403
  for (let i = 0; i < tx.inputs.length; i++) {
@@ -417,7 +416,7 @@ export const sweepOrdinals = {
417
416
  }
418
417
  console.log(`[sweepOrdinals] Total in: ${totalInputSats}, Total out: ${totalOutputSats}, Fee: ${totalInputSats - totalOutputSats}`);
419
418
  console.log(`[sweepOrdinals] Signable tx hex: ${Utils.toHex(createResult.signableTransaction.tx)}`);
420
- console.log(`[sweepOrdinals] ==============================`);
419
+ console.log("[sweepOrdinals] ==============================");
421
420
  // Build a set of outpoints we control
422
421
  const ourOutpoints = new Set(inputs.map((input) => {
423
422
  const [txid, vout] = input.outpoint.split("_");
@@ -435,12 +434,12 @@ export const sweepOrdinals = {
435
434
  await tx.sign();
436
435
  // Log signed transaction details for debugging
437
436
  const localTxid = tx.id("hex");
438
- console.log(`[sweepOrdinals] === LOCAL SIGNED TX ===`);
437
+ console.log("[sweepOrdinals] === LOCAL SIGNED TX ===");
439
438
  console.log(`[sweepOrdinals] Local txid: ${localTxid}`);
440
439
  console.log(`[sweepOrdinals] Signed tx hex: ${tx.toHex()}`);
441
440
  // Extract unlocking scripts for signAction
442
441
  const spends = {};
443
- console.log(`[sweepOrdinals] === UNLOCKING SCRIPTS FOR SIGNACTION ===`);
442
+ console.log("[sweepOrdinals] === UNLOCKING SCRIPTS FOR SIGNACTION ===");
444
443
  for (let i = 0; i < tx.inputs.length; i++) {
445
444
  const txInput = tx.inputs[i];
446
445
  const inputOutpoint = `${txInput.sourceTXID}.${txInput.sourceOutputIndex}`;
@@ -463,18 +462,18 @@ export const sweepOrdinals = {
463
462
  return { error: String(signResult.error) };
464
463
  }
465
464
  // Debug: compare local vs signAction result
466
- console.log(`[sweepOrdinals] === SIGN ACTION RESULT ===`);
465
+ console.log("[sweepOrdinals] === SIGN ACTION RESULT ===");
467
466
  console.log(`[sweepOrdinals] signAction txid: ${signResult.txid}`);
468
467
  // Log broadcast results if available
469
468
  if ("sendWithResults" in signResult) {
470
- console.log(`[sweepOrdinals] sendWithResults:`, JSON.stringify(signResult.sendWithResults));
469
+ console.log("[sweepOrdinals] sendWithResults:", JSON.stringify(signResult.sendWithResults));
471
470
  }
472
471
  console.log(`[sweepOrdinals] Local txid (partial): ${localTxid}`);
473
- console.log(`[sweepOrdinals] Note: TXIDs differ because local is partial (wallet input unsigned)`);
472
+ console.log("[sweepOrdinals] Note: TXIDs differ because local is partial (wallet input unsigned)");
474
473
  if (signResult.tx) {
475
474
  // Parse returned BEEF to show final transaction structure
476
475
  const returnedTx = Transaction.fromBEEF(signResult.tx);
477
- console.log(`[sweepOrdinals] === FINAL TX STRUCTURE (broadcast) ===`);
476
+ console.log("[sweepOrdinals] === FINAL TX STRUCTURE (broadcast) ===");
478
477
  console.log(`[sweepOrdinals] Final inputs (${returnedTx.inputs.length}):`);
479
478
  let returnedInputSats = 0;
480
479
  for (let i = 0; i < returnedTx.inputs.length; i++) {
@@ -501,7 +500,7 @@ export const sweepOrdinals = {
501
500
  const satPerByte = finalFee / txSize;
502
501
  console.log(`[sweepOrdinals] Tx size: ${txSize} bytes, Fee rate: ${satPerByte.toFixed(2)} sat/byte`);
503
502
  if (satPerByte < 0.5) {
504
- console.warn(`[sweepOrdinals] WARNING: Fee rate seems very low!`);
503
+ console.warn("[sweepOrdinals] WARNING: Fee rate seems very low!");
505
504
  }
506
505
  }
507
506
  return {
@@ -583,6 +582,12 @@ export const sweepBsv21 = {
583
582
  if (!inputs.every((i) => i.tokenId === tokenId)) {
584
583
  return { error: "mixed-token-ids" };
585
584
  }
585
+ // Lookup token details to verify it's active and get fee info
586
+ const tokenDetails = await ctx.services.bsv21.getTokenDetails(tokenId);
587
+ if (!tokenDetails.status.is_active) {
588
+ return { error: "token-not-active" };
589
+ }
590
+ const { fee_address, fee_per_output } = tokenDetails.status;
586
591
  // Parse WIF
587
592
  const privateKey = PrivateKey.fromWif(wif);
588
593
  // Sum all input amounts
@@ -636,11 +641,10 @@ export const sweepBsv21 = {
636
641
  keyID,
637
642
  }),
638
643
  });
639
- // 2. Fee output (1000 sats) to overlay fund address
640
- const fundAddress = deriveFundAddress(tokenId);
644
+ // 2. Fee output to overlay fund address
641
645
  outputs.push({
642
- lockingScript: p2pkh.lock(fundAddress).toHex(),
643
- satoshis: BSV21_FEE_SATS,
646
+ lockingScript: p2pkh.lock(fee_address).toHex(),
647
+ satoshis: fee_per_output,
644
648
  outputDescription: "Overlay processing fee",
645
649
  tags: [],
646
650
  });
@@ -698,13 +702,12 @@ export const sweepBsv21 = {
698
702
  // Submit to overlay service for indexing
699
703
  if (signResult.tx) {
700
704
  try {
701
- const services = ctx.services;
702
- const overlayResult = await services.overlay.submitBsv21(signResult.tx, tokenId);
703
- console.log(`[sweepBsv21] Overlay submission result:`, overlayResult);
705
+ const overlayResult = await ctx.services.overlay.submitBsv21(signResult.tx, tokenId);
706
+ console.log("[sweepBsv21] Overlay submission result:", overlayResult);
704
707
  }
705
708
  catch (overlayError) {
706
709
  // Log but don't fail the sweep - tx is already broadcast
707
- console.warn(`[sweepBsv21] Overlay submission failed:`, overlayError);
710
+ console.warn("[sweepBsv21] Overlay submission failed:", overlayError);
708
711
  }
709
712
  }
710
713
  return {
@@ -252,9 +252,12 @@ export const sendBsv21 = {
252
252
  else if (paymail) {
253
253
  return { error: "paymail-not-yet-implemented" };
254
254
  }
255
- else {
255
+ else if (address) {
256
256
  recipientAddress = address;
257
257
  }
258
+ else {
259
+ return { error: "must-provide-counterparty-or-address" };
260
+ }
258
261
  const outputs = [];
259
262
  const p2pkh = new P2PKH();
260
263
  const destinationLockingScript = p2pkh.lock(recipientAddress);
@@ -324,12 +327,11 @@ export const sendBsv21 = {
324
327
  // Submit to overlay service for indexing
325
328
  if (signResult.tx && ctx.services) {
326
329
  try {
327
- const services = ctx.services;
328
- const overlayResult = await services.overlay.submitBsv21(signResult.tx, tokenId);
329
- console.log(`[sendBsv21] Overlay submission result:`, overlayResult);
330
+ const overlayResult = await ctx.services.overlay.submitBsv21(signResult.tx, tokenId);
331
+ console.log("[sendBsv21] Overlay submission result:", overlayResult);
330
332
  }
331
333
  catch (overlayError) {
332
- console.warn(`[sendBsv21] Overlay submission failed:`, overlayError);
334
+ console.warn("[sendBsv21] Overlay submission failed:", overlayError);
333
335
  }
334
336
  }
335
337
  return {
@@ -488,12 +490,11 @@ export const purchaseBsv21 = {
488
490
  // Submit to overlay service for indexing
489
491
  if (signResult.tx && ctx.services) {
490
492
  try {
491
- const services = ctx.services;
492
- const overlayResult = await services.overlay.submitBsv21(signResult.tx, tokenId);
493
- console.log(`[purchaseBsv21] Overlay submission result:`, overlayResult);
493
+ const overlayResult = await ctx.services.overlay.submitBsv21(signResult.tx, tokenId);
494
+ console.log("[purchaseBsv21] Overlay submission result:", overlayResult);
494
495
  }
495
496
  catch (overlayError) {
496
- console.warn(`[purchaseBsv21] Overlay submission failed:`, overlayError);
497
+ console.warn("[purchaseBsv21] Overlay submission failed:", overlayError);
497
498
  }
498
499
  }
499
500
  return {
@@ -139,7 +139,7 @@ export class OneSatServices {
139
139
  }
140
140
  async postBeef(beef, txids) {
141
141
  console.log("[OneSatServices] postBeef called with txids:", txids);
142
- console.log("[OneSatServices] BEEF structure:\n" + beef.toLogString());
142
+ console.log(`[OneSatServices] BEEF structure:\n${beef.toLogString()}`);
143
143
  const results = [];
144
144
  for (const txid of txids) {
145
145
  try {
@@ -149,7 +149,7 @@ export class OneSatServices {
149
149
  console.log("[OneSatServices] AtomicBEEF length:", atomicBeef.length, "bytes");
150
150
  // Parse back to verify structure
151
151
  const verifyBeef = Beef.fromBinary(atomicBeef);
152
- console.log("[OneSatServices] AtomicBEEF parsed back:\n" + verifyBeef.toLogString());
152
+ console.log(`[OneSatServices] AtomicBEEF parsed back:\n${verifyBeef.toLogString()}`);
153
153
  // TODO: Remove hardcoded callback headers after server testing
154
154
  const status = await this.arcade.submitTransaction(atomicBeef, {
155
155
  callbackUrl: `${this.baseUrl}/1sat/arc/callback`,
@@ -292,10 +292,8 @@ export class OneSatServices {
292
292
  const depth = currentHeight - tx.merklePath.blockHeight + 1;
293
293
  return { txid, status: "mined", depth };
294
294
  }
295
- else {
296
- // No merkle path = known but not yet mined
297
- return { txid, status: "known", depth: 0 };
298
- }
295
+ // No merkle path = known but not yet mined
296
+ return { txid, status: "known", depth: 0 };
299
297
  }
300
298
  catch {
301
299
  // 404 or error from Beef = unknown
@@ -81,13 +81,13 @@ export interface SyncProcessorOptions {
81
81
  indexers?: Indexer[];
82
82
  }
83
83
  export interface SyncProcessorEvents {
84
- "process:start": {};
84
+ "process:start": Record<string, never>;
85
85
  "process:progress": {
86
86
  pending: number;
87
87
  done: number;
88
88
  failed: number;
89
89
  };
90
- "process:complete": {};
90
+ "process:complete": Record<string, never>;
91
91
  "process:error": {
92
92
  message: string;
93
93
  };
@@ -161,7 +161,7 @@ export interface SyncEvents {
161
161
  done: number;
162
162
  failed: number;
163
163
  };
164
- "sync:complete": {};
164
+ "sync:complete": Record<string, never>;
165
165
  "sync:error": {
166
166
  message: string;
167
167
  };
@@ -44,7 +44,7 @@ export class SyncFetcher {
44
44
  }
45
45
  this.listeners
46
46
  .get(event)
47
- .add(listener);
47
+ ?.add(listener);
48
48
  }
49
49
  off(event, listener) {
50
50
  this.listeners
@@ -52,7 +52,9 @@ export class SyncFetcher {
52
52
  ?.delete(listener);
53
53
  }
54
54
  emit(event, data) {
55
- this.listeners.get(event)?.forEach((listener) => listener(data));
55
+ for (const listener of this.listeners.get(event) ?? []) {
56
+ listener(data);
57
+ }
56
58
  }
57
59
  /**
58
60
  * Fetch new outputs via SSE and enqueue them.
@@ -161,7 +163,7 @@ export class SyncProcessor {
161
163
  }
162
164
  this.listeners
163
165
  .get(event)
164
- .add(listener);
166
+ ?.add(listener);
165
167
  }
166
168
  off(event, listener) {
167
169
  this.listeners
@@ -169,7 +171,9 @@ export class SyncProcessor {
169
171
  ?.delete(listener);
170
172
  }
171
173
  emit(event, data) {
172
- this.listeners.get(event)?.forEach((listener) => listener(data));
174
+ for (const listener of this.listeners.get(event) ?? []) {
175
+ listener(data);
176
+ }
173
177
  }
174
178
  /**
175
179
  * Start processing the queue.
@@ -379,18 +383,16 @@ export class SyncProcessor {
379
383
  },
380
384
  };
381
385
  }
382
- else {
383
- // P2PKH-based output - use wallet payment for auto-signing
384
- return {
385
- outputIndex: vout,
386
- protocol: "wallet payment",
387
- paymentRemittance: {
388
- derivationPrefix: derivation.derivationPrefix,
389
- derivationSuffix: derivation.derivationSuffix,
390
- senderIdentityKey: derivation.senderIdentityKey,
391
- },
392
- };
393
- }
386
+ // P2PKH-based output - use wallet payment for auto-signing
387
+ return {
388
+ outputIndex: vout,
389
+ protocol: "wallet payment",
390
+ paymentRemittance: {
391
+ derivationPrefix: derivation.derivationPrefix,
392
+ derivationSuffix: derivation.derivationSuffix,
393
+ senderIdentityKey: derivation.senderIdentityKey,
394
+ },
395
+ };
394
396
  }
395
397
  collectTags(txo) {
396
398
  const tags = [];
@@ -467,7 +469,7 @@ export class SyncManager {
467
469
  }
468
470
  this.listeners
469
471
  .get(event)
470
- .add(listener);
472
+ ?.add(listener);
471
473
  }
472
474
  off(event, listener) {
473
475
  this.listeners
@@ -475,7 +477,9 @@ export class SyncManager {
475
477
  ?.delete(listener);
476
478
  }
477
479
  emit(event, data) {
478
- this.listeners.get(event)?.forEach((listener) => listener(data));
480
+ for (const listener of this.listeners.get(event) ?? []) {
481
+ listener(data);
482
+ }
479
483
  }
480
484
  // ===== Sync Control =====
481
485
  /**
@@ -73,7 +73,7 @@ export async function createWebWallet(config) {
73
73
  const localStorage = new StorageIdb(storageOptions);
74
74
  await localStorage.migrate(DEFAULT_DATABASE_NAME, config.storageIdentityKey);
75
75
  // 4. Create storage manager with local-only storage initially (empty backups)
76
- let storage = new WalletStorageManager(identityPubKey, localStorage, []);
76
+ const storage = new WalletStorageManager(identityPubKey, localStorage, []);
77
77
  await storage.makeAvailable();
78
78
  // 5. Create the underlying Wallet FIRST (needed for StorageClient signing)
79
79
  const underlyingWallet = new Wallet({
@@ -175,7 +175,7 @@ export async function createWebWallet(config) {
175
175
  unprovenAttemptsLimitMain: 144,
176
176
  });
177
177
  monitor.addDefaultTasks();
178
- console.log("[createWebWallet] Monitor created with tasks:", monitor["_tasks"].map((t) => t.name));
178
+ console.log("[createWebWallet] Monitor created with tasks:", monitor._tasks.map((t) => t.name));
179
179
  // 11. Wire up monitor callbacks - sync to remote first, then call user callbacks
180
180
  // Note: For delayed broadcasts, the monitor triggers these. For immediate broadcasts,
181
181
  // the interception in step 8 handles the sync, but these still fire for the user callback.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1sat/wallet-toolbox",
3
- "version": "0.0.74",
3
+ "version": "0.0.75",
4
4
  "description": "BSV wallet library extending @bsv/wallet-toolbox with 1Sat Ordinals protocol support",
5
5
  "author": "1Sat Team",
6
6
  "license": "MIT",