@1sat/wallet-toolbox 0.0.8 → 0.0.10

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 (64) hide show
  1. package/dist/api/balance/index.d.ts +50 -0
  2. package/dist/api/balance/index.js +135 -0
  3. package/dist/api/broadcast/index.d.ts +24 -0
  4. package/dist/api/broadcast/index.js +73 -0
  5. package/dist/api/constants.d.ts +21 -0
  6. package/dist/api/constants.js +29 -0
  7. package/dist/api/index.d.ts +36 -0
  8. package/dist/api/index.js +59 -0
  9. package/dist/api/inscriptions/index.d.ts +25 -0
  10. package/dist/api/inscriptions/index.js +98 -0
  11. package/dist/api/locks/index.d.ts +47 -0
  12. package/dist/api/locks/index.js +291 -0
  13. package/dist/api/ordinals/index.d.ts +102 -0
  14. package/dist/api/ordinals/index.js +566 -0
  15. package/dist/api/payments/index.d.ts +48 -0
  16. package/dist/api/payments/index.js +185 -0
  17. package/dist/api/signing/index.d.ts +35 -0
  18. package/dist/api/signing/index.js +78 -0
  19. package/dist/api/skills/registry.d.ts +61 -0
  20. package/dist/api/skills/registry.js +74 -0
  21. package/dist/api/skills/types.d.ts +71 -0
  22. package/dist/api/skills/types.js +14 -0
  23. package/dist/api/tokens/index.d.ts +87 -0
  24. package/dist/api/tokens/index.js +457 -0
  25. package/dist/cwi/chrome.d.ts +11 -0
  26. package/dist/cwi/chrome.js +39 -0
  27. package/dist/cwi/event.d.ts +11 -0
  28. package/dist/cwi/event.js +38 -0
  29. package/dist/cwi/factory.d.ts +14 -0
  30. package/dist/cwi/factory.js +44 -0
  31. package/dist/cwi/index.d.ts +11 -0
  32. package/dist/cwi/index.js +11 -0
  33. package/dist/cwi/types.d.ts +39 -0
  34. package/dist/cwi/types.js +39 -0
  35. package/dist/index.d.ts +3 -1
  36. package/dist/index.js +7 -1
  37. package/dist/indexers/CosignIndexer.js +1 -0
  38. package/dist/indexers/InscriptionIndexer.js +3 -4
  39. package/dist/indexers/LockIndexer.js +1 -0
  40. package/dist/indexers/OrdLockIndexer.js +1 -0
  41. package/dist/indexers/OriginIndexer.js +1 -1
  42. package/dist/indexers/index.d.ts +1 -1
  43. package/dist/indexers/types.d.ts +18 -0
  44. package/dist/services/OneSatServices.d.ts +19 -10
  45. package/dist/services/OneSatServices.js +201 -39
  46. package/dist/services/client/ChaintracksClient.d.ts +55 -13
  47. package/dist/services/client/ChaintracksClient.js +123 -28
  48. package/dist/services/client/OrdfsClient.d.ts +2 -2
  49. package/dist/services/client/OrdfsClient.js +4 -3
  50. package/dist/services/client/TxoClient.js +9 -0
  51. package/dist/sync/AddressManager.d.ts +85 -0
  52. package/dist/sync/AddressManager.js +107 -0
  53. package/dist/sync/SyncManager.d.ts +207 -0
  54. package/dist/sync/SyncManager.js +507 -0
  55. package/dist/sync/index.d.ts +4 -0
  56. package/dist/sync/index.js +2 -0
  57. package/dist/wallet/factory.d.ts +64 -0
  58. package/dist/wallet/factory.js +129 -0
  59. package/dist/wallet/index.d.ts +1 -0
  60. package/dist/wallet/index.js +1 -0
  61. package/package.json +14 -4
  62. package/dist/OneSatWallet.d.ts +0 -316
  63. package/dist/OneSatWallet.js +0 -956
  64. package/dist/indexers/TransactionParser.d.ts +0 -53
@@ -0,0 +1,39 @@
1
+ /**
2
+ * CWI (Chrome Wallet Interface) - Shared types for BRC-100 WalletInterface implementations
3
+ */
4
+ export declare enum CWIEventName {
5
+ LIST_OUTPUTS = "cwi_listOutputs",
6
+ LIST_ACTIONS = "cwi_listActions",
7
+ GET_PUBLIC_KEY = "cwi_getPublicKey",
8
+ GET_HEIGHT = "cwi_getHeight",
9
+ GET_HEADER_FOR_HEIGHT = "cwi_getHeaderForHeight",
10
+ GET_NETWORK = "cwi_getNetwork",
11
+ GET_VERSION = "cwi_getVersion",
12
+ IS_AUTHENTICATED = "cwi_isAuthenticated",
13
+ WAIT_FOR_AUTHENTICATION = "cwi_waitForAuthentication",
14
+ CREATE_ACTION = "cwi_createAction",
15
+ SIGN_ACTION = "cwi_signAction",
16
+ ABORT_ACTION = "cwi_abortAction",
17
+ INTERNALIZE_ACTION = "cwi_internalizeAction",
18
+ CREATE_SIGNATURE = "cwi_createSignature",
19
+ VERIFY_SIGNATURE = "cwi_verifySignature",
20
+ ENCRYPT = "cwi_encrypt",
21
+ DECRYPT = "cwi_decrypt",
22
+ CREATE_HMAC = "cwi_createHmac",
23
+ VERIFY_HMAC = "cwi_verifyHmac",
24
+ RELINQUISH_OUTPUT = "cwi_relinquishOutput",
25
+ ACQUIRE_CERTIFICATE = "cwi_acquireCertificate",
26
+ LIST_CERTIFICATES = "cwi_listCertificates",
27
+ PROVE_CERTIFICATE = "cwi_proveCertificate",
28
+ RELINQUISH_CERTIFICATE = "cwi_relinquishCertificate",
29
+ DISCOVER_BY_IDENTITY_KEY = "cwi_discoverByIdentityKey",
30
+ DISCOVER_BY_ATTRIBUTES = "cwi_discoverByAttributes",
31
+ REVEAL_COUNTERPARTY_KEY_LINKAGE = "cwi_revealCounterpartyKeyLinkage",
32
+ REVEAL_SPECIFIC_KEY_LINKAGE = "cwi_revealSpecificKeyLinkage"
33
+ }
34
+ export interface CWIResponseDetail<T = unknown> {
35
+ type: CWIEventName;
36
+ success: boolean;
37
+ data?: T;
38
+ error?: string;
39
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * CWI (Chrome Wallet Interface) - Shared types for BRC-100 WalletInterface implementations
3
+ */
4
+ // BRC-100 Event Names - shared between all CWI implementations
5
+ export var CWIEventName;
6
+ (function (CWIEventName) {
7
+ // Read-only operations
8
+ CWIEventName["LIST_OUTPUTS"] = "cwi_listOutputs";
9
+ CWIEventName["LIST_ACTIONS"] = "cwi_listActions";
10
+ CWIEventName["GET_PUBLIC_KEY"] = "cwi_getPublicKey";
11
+ CWIEventName["GET_HEIGHT"] = "cwi_getHeight";
12
+ CWIEventName["GET_HEADER_FOR_HEIGHT"] = "cwi_getHeaderForHeight";
13
+ CWIEventName["GET_NETWORK"] = "cwi_getNetwork";
14
+ CWIEventName["GET_VERSION"] = "cwi_getVersion";
15
+ CWIEventName["IS_AUTHENTICATED"] = "cwi_isAuthenticated";
16
+ CWIEventName["WAIT_FOR_AUTHENTICATION"] = "cwi_waitForAuthentication";
17
+ // Signing operations (require password)
18
+ CWIEventName["CREATE_ACTION"] = "cwi_createAction";
19
+ CWIEventName["SIGN_ACTION"] = "cwi_signAction";
20
+ CWIEventName["ABORT_ACTION"] = "cwi_abortAction";
21
+ CWIEventName["INTERNALIZE_ACTION"] = "cwi_internalizeAction";
22
+ CWIEventName["CREATE_SIGNATURE"] = "cwi_createSignature";
23
+ CWIEventName["VERIFY_SIGNATURE"] = "cwi_verifySignature";
24
+ CWIEventName["ENCRYPT"] = "cwi_encrypt";
25
+ CWIEventName["DECRYPT"] = "cwi_decrypt";
26
+ CWIEventName["CREATE_HMAC"] = "cwi_createHmac";
27
+ CWIEventName["VERIFY_HMAC"] = "cwi_verifyHmac";
28
+ CWIEventName["RELINQUISH_OUTPUT"] = "cwi_relinquishOutput";
29
+ // Certificate operations
30
+ CWIEventName["ACQUIRE_CERTIFICATE"] = "cwi_acquireCertificate";
31
+ CWIEventName["LIST_CERTIFICATES"] = "cwi_listCertificates";
32
+ CWIEventName["PROVE_CERTIFICATE"] = "cwi_proveCertificate";
33
+ CWIEventName["RELINQUISH_CERTIFICATE"] = "cwi_relinquishCertificate";
34
+ CWIEventName["DISCOVER_BY_IDENTITY_KEY"] = "cwi_discoverByIdentityKey";
35
+ CWIEventName["DISCOVER_BY_ATTRIBUTES"] = "cwi_discoverByAttributes";
36
+ // Key linkage
37
+ CWIEventName["REVEAL_COUNTERPARTY_KEY_LINKAGE"] = "cwi_revealCounterpartyKeyLinkage";
38
+ CWIEventName["REVEAL_SPECIFIC_KEY_LINKAGE"] = "cwi_revealSpecificKeyLinkage";
39
+ })(CWIEventName || (CWIEventName = {}));
package/dist/index.d.ts CHANGED
@@ -1,7 +1,9 @@
1
- export { OneSatWallet, type OneSatWalletArgs, type OneSatWalletEvents, type IngestResult, } from "./OneSatWallet";
1
+ export * from "./api";
2
2
  export { OneSatServices, type SyncOutput } from "./services/OneSatServices";
3
3
  export type { OrdfsMetadata, OrdfsContentOptions, OrdfsContentResponse, OrdfsResponseHeaders, Capability, } from "./services/types";
4
4
  export * from "./services/client";
5
5
  export { ReadOnlySigner } from "./signers/ReadOnlySigner";
6
6
  export * from "./indexers";
7
7
  export * from "./sync";
8
+ export * from "./cwi";
9
+ export * from "./wallet";
package/dist/index.js CHANGED
@@ -3,7 +3,9 @@ import { Buffer } from "buffer";
3
3
  if (typeof globalThis.Buffer === "undefined") {
4
4
  globalThis.Buffer = Buffer;
5
5
  }
6
- export { OneSatWallet, } from "./OneSatWallet";
6
+ // API Layer - The new unified API for 1sat ecosystem
7
+ export * from "./api";
8
+ // Services
7
9
  export { OneSatServices } from "./services/OneSatServices";
8
10
  export * from "./services/client";
9
11
  export { ReadOnlySigner } from "./signers/ReadOnlySigner";
@@ -11,3 +13,7 @@ export { ReadOnlySigner } from "./signers/ReadOnlySigner";
11
13
  export * from "./indexers";
12
14
  // Sync Queue
13
15
  export * from "./sync";
16
+ // CWI (Chrome Wallet Interface) - BRC-100 implementations
17
+ export * from "./cwi";
18
+ // Wallet factory for creating 1Sat web wallets
19
+ export * from "./wallet";
@@ -20,6 +20,7 @@ export class CosignIndexer extends Indexer {
20
20
  data: decoded,
21
21
  tags: [],
22
22
  owner: decoded.address,
23
+ protocol: "basket insertion", // Cosign script requires cosigner signature
23
24
  };
24
25
  }
25
26
  }
@@ -35,11 +35,10 @@ export class InscriptionIndexer extends Indexer {
35
35
  let owner = parseAddress(script, 0, this.network);
36
36
  if (!owner && decoded.scriptSuffix) {
37
37
  // Try to find owner in suffix (after OP_ENDIF)
38
- const suffixScript = Script.fromBinary(Array.from(decoded.scriptSuffix));
39
- owner = parseAddress(suffixScript, 0, this.network);
38
+ owner = parseAddress(decoded.scriptSuffix, 0, this.network);
40
39
  // Also check for OP_CODESEPARATOR pattern
41
- if (!owner && suffixScript.chunks[0]?.op === OP.OP_CODESEPARATOR) {
42
- owner = parseAddress(suffixScript, 1, this.network);
40
+ if (!owner && decoded.scriptSuffix.chunks[0]?.op === OP.OP_CODESEPARATOR) {
41
+ owner = parseAddress(decoded.scriptSuffix, 1, this.network);
43
42
  }
44
43
  }
45
44
  // Handle MAP field if present (special case)
@@ -17,6 +17,7 @@ export class LockIndexer extends Indexer {
17
17
  tags,
18
18
  owner: decoded.address,
19
19
  basket: "lock",
20
+ protocol: "basket insertion", // Custom Lock script requires manual unlock
20
21
  };
21
22
  }
22
23
  async summarize(ctx) {
@@ -28,6 +28,7 @@ export class OrdLockIndexer extends Indexer {
28
28
  data: listing,
29
29
  tags: [`list:${listing.price}`],
30
30
  owner: decoded.seller,
31
+ protocol: "basket insertion", // OrdLock script requires manual unlock
31
32
  };
32
33
  }
33
34
  async summarize(ctx) {
@@ -47,7 +47,7 @@ export class OriginIndexer extends Indexer {
47
47
  return {
48
48
  data: origin,
49
49
  tags: [], // Tags will be added in summarize() once origin is determined
50
- owner: address && this.owners.has(address) ? address : undefined,
50
+ owner: address,
51
51
  basket: "1sat",
52
52
  };
53
53
  }
@@ -1,4 +1,4 @@
1
- export { Indexer, type IndexData, type IndexSummary, type ParseContext, type ParseResult, type Txo, } from "./types";
1
+ export { Indexer, type IndexData, type IndexSummary, type InternalizeProtocol, type ParseContext, type ParseResult, type Txo, } from "./types";
2
2
  export type { Bsv21TokenData, Bsv21OutputData, Bsv21TransactionData, } from "./types";
3
3
  export { Outpoint } from "./Outpoint";
4
4
  export { parseAddress } from "./parseAddress";
@@ -54,6 +54,12 @@ export interface IndexSummary {
54
54
  icon?: string;
55
55
  data?: unknown;
56
56
  }
57
+ /**
58
+ * Internalization protocol for synced outputs.
59
+ * - "wallet payment": P2PKH-based outputs that can be auto-signed
60
+ * - "basket insertion": Custom script outputs requiring manual unlock scripts
61
+ */
62
+ export type InternalizeProtocol = "wallet payment" | "basket insertion";
57
63
  /**
58
64
  * Result from Indexer.parse() method
59
65
  */
@@ -64,6 +70,12 @@ export interface ParseResult {
64
70
  basket?: string;
65
71
  /** Optional text content (e.g., from text inscriptions). Truncated to 1000 chars when stored. */
66
72
  content?: string;
73
+ /**
74
+ * Internalization protocol for this output.
75
+ * - "wallet payment": P2PKH outputs that can be auto-signed (default)
76
+ * - "basket insertion": Custom script outputs requiring manual unlock
77
+ */
78
+ protocol?: InternalizeProtocol;
67
79
  }
68
80
  /**
69
81
  * Transaction output structure used during parsing
@@ -73,6 +85,12 @@ export interface Txo {
73
85
  outpoint: Outpoint;
74
86
  owner?: string;
75
87
  basket?: string;
88
+ /**
89
+ * Internalization protocol for this output.
90
+ * Set by the first indexer to claim ownership.
91
+ * Defaults to "wallet payment" if not set.
92
+ */
93
+ protocol?: InternalizeProtocol;
76
94
  data: {
77
95
  [tag: string]: IndexData;
78
96
  };
@@ -1,5 +1,5 @@
1
1
  import { Beef, Transaction } from "@bsv/sdk";
2
- import type { TableOutput, WalletStorageManager, sdk as toolboxSdk } from "@bsv/wallet-toolbox";
2
+ import { type TableOutput, type sdk as toolboxSdk } from "@bsv/wallet-toolbox";
3
3
  type Chain = toolboxSdk.Chain;
4
4
  type BlockHeader = toolboxSdk.BlockHeader;
5
5
  type GetMerklePathResult = toolboxSdk.GetMerklePathResult;
@@ -32,7 +32,6 @@ export type { SyncOutput };
32
32
  export declare class OneSatServices implements WalletServices {
33
33
  chain: Chain;
34
34
  readonly baseUrl: string;
35
- private storage?;
36
35
  readonly chaintracks: ChaintracksClient;
37
36
  readonly beef: BeefClient;
38
37
  readonly arcade: ArcadeClient;
@@ -40,7 +39,13 @@ export declare class OneSatServices implements WalletServices {
40
39
  readonly owner: OwnerClient;
41
40
  readonly ordfs: OrdfsClient;
42
41
  readonly bsv21: Bsv21Client;
43
- constructor(chain: Chain, baseUrl?: string, storage?: WalletStorageManager);
42
+ private fallbackServices?;
43
+ /**
44
+ * URL for wallet storage sync endpoint (BRC-100 JSON-RPC).
45
+ * Used by StorageClient for remote wallet backup/sync.
46
+ */
47
+ get storageUrl(): string;
48
+ constructor(chain: Chain, baseUrl?: string, fallbackServices?: WalletServices);
44
49
  /**
45
50
  * Get list of enabled capabilities from the server
46
51
  */
@@ -59,11 +64,15 @@ export declare class OneSatServices implements WalletServices {
59
64
  hashOutputScript(script: string): string;
60
65
  getServicesCallHistory(_reset?: boolean): ServicesCallHistory;
61
66
  getBsvExchangeRate(): Promise<number>;
62
- getFiatExchangeRate(_currency: "USD" | "GBP" | "EUR", _base?: "USD" | "GBP" | "EUR"): Promise<number>;
63
- getStatusForTxids(_txids: string[], _useNext?: boolean): Promise<GetStatusForTxidsResult>;
64
- isUtxo(_output: TableOutput): Promise<boolean>;
65
- getUtxoStatus(_output: string, _outputFormat?: GetUtxoStatusOutputFormat, _outpoint?: string, _useNext?: boolean): Promise<GetUtxoStatusResult>;
66
- getScriptHashHistory(_hash: string, _useNext?: boolean): Promise<GetScriptHashHistoryResult>;
67
- hashToHeader(_hash: string): Promise<BlockHeader>;
68
- nLockTimeIsFinal(_txOrLockTime: string | number[] | Transaction | number): Promise<boolean>;
67
+ getFiatExchangeRate(currency: "USD" | "GBP" | "EUR", base?: "USD" | "GBP" | "EUR"): Promise<number>;
68
+ getStatusForTxids(txids: string[], _useNext?: boolean): Promise<GetStatusForTxidsResult>;
69
+ /**
70
+ * Helper to get tx status from Beef storage (fallback when Arcade doesn't know the tx)
71
+ */
72
+ private getStatusFromBeef;
73
+ isUtxo(output: TableOutput): Promise<boolean>;
74
+ getUtxoStatus(_output: string, _outputFormat?: GetUtxoStatusOutputFormat, outpoint?: string, _useNext?: boolean): Promise<GetUtxoStatusResult>;
75
+ getScriptHashHistory(hash: string, useNext?: boolean): Promise<GetScriptHashHistoryResult>;
76
+ hashToHeader(hash: string): Promise<BlockHeader>;
77
+ nLockTimeIsFinal(txOrLockTime: string | number[] | Transaction | number): Promise<boolean>;
69
78
  }
@@ -32,7 +32,6 @@ import { ArcadeClient, BeefClient, Bsv21Client, ChaintracksClient, OrdfsClient,
32
32
  export class OneSatServices {
33
33
  chain;
34
34
  baseUrl;
35
- storage;
36
35
  // ===== API Clients =====
37
36
  chaintracks;
38
37
  beef;
@@ -41,14 +40,23 @@ export class OneSatServices {
41
40
  owner;
42
41
  ordfs;
43
42
  bsv21;
44
- constructor(chain, baseUrl, storage) {
43
+ // Optional fallback to wallet-toolbox Services for methods we don't implement
44
+ fallbackServices;
45
+ /**
46
+ * URL for wallet storage sync endpoint (BRC-100 JSON-RPC).
47
+ * Used by StorageClient for remote wallet backup/sync.
48
+ */
49
+ get storageUrl() {
50
+ return `${this.baseUrl}/1sat/wallet`;
51
+ }
52
+ constructor(chain, baseUrl, fallbackServices) {
53
+ this.fallbackServices = fallbackServices;
45
54
  this.chain = chain;
46
55
  this.baseUrl =
47
56
  baseUrl ||
48
57
  (chain === "main"
49
58
  ? "https://1sat.shruggr.cloud"
50
59
  : "https://testnet.api.1sat.app");
51
- this.storage = storage;
52
60
  const opts = { timeout: 30000 };
53
61
  this.chaintracks = new ChaintracksClient(this.baseUrl, opts);
54
62
  this.beef = new BeefClient(this.baseUrl, opts);
@@ -101,12 +109,25 @@ export class OneSatServices {
101
109
  return this.chaintracks.currentHeight();
102
110
  }
103
111
  async getMerklePath(txid, _useNext) {
112
+ console.log("[OneSatServices] getMerklePath called for txid:", txid);
104
113
  try {
105
114
  const proofBytes = await this.beef.getProof(txid);
106
- const merklePath = MerklePath.fromBinary(Array.from(proofBytes));
107
- return { name: "1sat-api", merklePath };
115
+ const merklePath = MerklePath.fromBinary([...proofBytes]);
116
+ console.log("[OneSatServices] getMerklePath got proof, blockHeight:", merklePath.blockHeight);
117
+ // Fetch the block header for this merkle path
118
+ const header = await this.chaintracks.findHeaderForHeight(merklePath.blockHeight);
119
+ if (!header) {
120
+ console.log("[OneSatServices] getMerklePath header not found for height:", merklePath.blockHeight);
121
+ return {
122
+ name: "1sat-api",
123
+ error: new ServiceError("HEADER_NOT_FOUND", `Block header not found for height ${merklePath.blockHeight}`),
124
+ };
125
+ }
126
+ console.log("[OneSatServices] getMerklePath success, returning merklePath and header");
127
+ return { name: "1sat-api", merklePath, header };
108
128
  }
109
129
  catch (error) {
130
+ console.error("[OneSatServices] getMerklePath error:", error);
110
131
  return {
111
132
  name: "1sat-api",
112
133
  error: new ServiceError("NETWORK_ERROR", error instanceof Error ? error.message : "Unknown error"),
@@ -114,27 +135,19 @@ export class OneSatServices {
114
135
  }
115
136
  }
116
137
  async postBeef(beef, txids) {
138
+ console.log("[OneSatServices] postBeef called with txids:", txids);
117
139
  const results = [];
118
140
  for (const txid of txids) {
119
141
  try {
120
- const beefTx = beef.findTxid(txid);
121
- if (!beefTx?.tx) {
122
- results.push({
123
- name: "1sat-api",
124
- status: "error",
125
- error: new ServiceError("TX_NOT_FOUND", `Transaction ${txid} not found in BEEF`),
126
- txidResults: [
127
- {
128
- txid,
129
- status: "error",
130
- data: { detail: "Transaction not found in BEEF" },
131
- },
132
- ],
133
- });
134
- continue;
135
- }
136
- // Use Extended Format (EF) which includes source transaction data
137
- const status = await this.arcade.submitTransaction(beefTx.tx.toEF());
142
+ // Submit as AtomicBEEF which includes all source transactions
143
+ console.log("[OneSatServices] Submitting tx to arcade:", txid);
144
+ const atomicBeef = beef.toBinaryAtomic(txid);
145
+ // TODO: Remove hardcoded callback headers after server testing
146
+ const status = await this.arcade.submitTransaction(atomicBeef, {
147
+ callbackUrl: `${this.baseUrl}/1sat/arc/callback`,
148
+ callbackToken: "test-callback-token",
149
+ });
150
+ console.log("[OneSatServices] Arcade response:", status);
138
151
  if (status.txStatus === "MINED" ||
139
152
  status.txStatus === "SEEN_ON_NETWORK" ||
140
153
  status.txStatus === "ACCEPTED_BY_NETWORK") {
@@ -163,6 +176,7 @@ export class OneSatServices {
163
176
  }
164
177
  }
165
178
  catch (error) {
179
+ console.error("[OneSatServices] postBeef error:", error);
166
180
  results.push({
167
181
  name: "1sat-api",
168
182
  status: "error",
@@ -203,29 +217,177 @@ export class OneSatServices {
203
217
  },
204
218
  };
205
219
  }
206
- // ===== WalletServices Interface (Not Yet Implemented) =====
220
+ // ===== WalletServices Interface (Delegated to fallback) =====
207
221
  async getBsvExchangeRate() {
208
- throw new Error("getBsvExchangeRate not yet implemented");
222
+ if (!this.fallbackServices) {
223
+ throw new Error("getBsvExchangeRate not implemented");
224
+ }
225
+ return this.fallbackServices.getBsvExchangeRate();
209
226
  }
210
- async getFiatExchangeRate(_currency, _base) {
211
- throw new Error("getFiatExchangeRate not yet implemented");
227
+ async getFiatExchangeRate(currency, base) {
228
+ if (!this.fallbackServices) {
229
+ throw new Error("getFiatExchangeRate not implemented");
230
+ }
231
+ return this.fallbackServices.getFiatExchangeRate(currency, base);
212
232
  }
213
- async getStatusForTxids(_txids, _useNext) {
214
- throw new Error("getStatusForTxids not yet implemented");
233
+ async getStatusForTxids(txids, _useNext) {
234
+ const results = [];
235
+ let currentHeight;
236
+ for (const txid of txids) {
237
+ try {
238
+ // Try Arcade first (only knows about txs broadcast through it)
239
+ const status = await this.arcade.getStatus(txid);
240
+ if (status.txStatus === "MINED" || status.txStatus === "IMMUTABLE") {
241
+ // Get current height for depth calculation if we haven't already
242
+ if (currentHeight === undefined) {
243
+ currentHeight = await this.getHeight();
244
+ }
245
+ const depth = status.blockHeight
246
+ ? currentHeight - status.blockHeight + 1
247
+ : 1;
248
+ results.push({ txid, status: "mined", depth });
249
+ }
250
+ else if (status.txStatus === "SEEN_ON_NETWORK" ||
251
+ status.txStatus === "ACCEPTED_BY_NETWORK" ||
252
+ status.txStatus === "SENT_TO_NETWORK" ||
253
+ status.txStatus === "RECEIVED") {
254
+ results.push({ txid, status: "known", depth: 0 });
255
+ }
256
+ else {
257
+ // REJECTED, DOUBLE_SPEND_ATTEMPTED, UNKNOWN - fall back to Beef
258
+ results.push(await this.getStatusFromBeef(txid));
259
+ }
260
+ }
261
+ catch {
262
+ // Arcade 404 or error - fall back to Beef storage
263
+ // NOTE: If Arcade's scope is too limited (only knows txs it broadcast),
264
+ // consider using Beef storage as the primary source instead.
265
+ results.push(await this.getStatusFromBeef(txid));
266
+ }
267
+ }
268
+ return {
269
+ name: "1sat-api",
270
+ status: "success",
271
+ results,
272
+ };
215
273
  }
216
- async isUtxo(_output) {
217
- throw new Error("isUtxo not yet implemented");
274
+ /**
275
+ * Helper to get tx status from Beef storage (fallback when Arcade doesn't know the tx)
276
+ */
277
+ async getStatusFromBeef(txid) {
278
+ try {
279
+ const beefBytes = await this.beef.getBeef(txid);
280
+ const tx = Transaction.fromBEEF(Array.from(beefBytes));
281
+ if (tx.merklePath) {
282
+ // Has a valid merkle path = mined
283
+ const currentHeight = await this.getHeight();
284
+ const depth = currentHeight - tx.merklePath.blockHeight + 1;
285
+ return { txid, status: "mined", depth };
286
+ }
287
+ else {
288
+ // No merkle path = known but not yet mined
289
+ return { txid, status: "known", depth: 0 };
290
+ }
291
+ }
292
+ catch {
293
+ // 404 or error from Beef = unknown
294
+ return { txid, status: "unknown", depth: undefined };
295
+ }
296
+ }
297
+ async isUtxo(output) {
298
+ const outpoint = `${output.txid}_${output.vout}`;
299
+ const spendTxid = await this.txo.getSpend(outpoint);
300
+ return spendTxid === null;
218
301
  }
219
- async getUtxoStatus(_output, _outputFormat, _outpoint, _useNext) {
220
- throw new Error("getUtxoStatus not yet implemented");
302
+ async getUtxoStatus(_output, _outputFormat, outpoint, _useNext) {
303
+ // We ignore _output (script hash) since we look up directly by outpoint
304
+ if (!outpoint) {
305
+ return {
306
+ name: "1sat-api",
307
+ status: "error",
308
+ error: new ServiceError("INVALID_PARAMETER", "outpoint is required for getUtxoStatus"),
309
+ details: [],
310
+ };
311
+ }
312
+ try {
313
+ const txo = await this.txo.get(outpoint, {
314
+ sats: true,
315
+ spend: true,
316
+ block: true,
317
+ });
318
+ const isUtxo = !txo.spend;
319
+ const [txid, voutStr] = txo.outpoint.split("_");
320
+ const vout = parseInt(voutStr, 10);
321
+ return {
322
+ name: "1sat-api",
323
+ status: "success",
324
+ isUtxo,
325
+ details: isUtxo
326
+ ? [
327
+ {
328
+ txid,
329
+ index: vout,
330
+ satoshis: txo.satoshis,
331
+ height: txo.blockHeight,
332
+ },
333
+ ]
334
+ : [],
335
+ };
336
+ }
337
+ catch {
338
+ // TXO not found - treat as not a UTXO
339
+ return {
340
+ name: "1sat-api",
341
+ status: "success",
342
+ isUtxo: false,
343
+ details: [],
344
+ };
345
+ }
221
346
  }
222
- async getScriptHashHistory(_hash, _useNext) {
223
- throw new Error("getScriptHashHistory not yet implemented");
347
+ async getScriptHashHistory(hash, useNext) {
348
+ if (!this.fallbackServices) {
349
+ throw new Error("getScriptHashHistory not implemented");
350
+ }
351
+ return this.fallbackServices.getScriptHashHistory(hash, useNext);
224
352
  }
225
- async hashToHeader(_hash) {
226
- throw new Error("hashToHeader not yet implemented");
353
+ async hashToHeader(hash) {
354
+ const header = await this.chaintracks.findHeaderForBlockHash(hash);
355
+ if (!header) {
356
+ throw new Error(`Block header not found for hash: ${hash}`);
357
+ }
358
+ return header;
227
359
  }
228
- async nLockTimeIsFinal(_txOrLockTime) {
229
- throw new Error("nLockTimeIsFinal not yet implemented");
360
+ async nLockTimeIsFinal(txOrLockTime) {
361
+ const MAXINT = 0xffffffff;
362
+ const BLOCK_LIMIT = 500000000;
363
+ let nLockTime;
364
+ if (typeof txOrLockTime === "number") {
365
+ nLockTime = txOrLockTime;
366
+ }
367
+ else {
368
+ let tx;
369
+ if (typeof txOrLockTime === "string") {
370
+ tx = Transaction.fromHex(txOrLockTime);
371
+ }
372
+ else if (Array.isArray(txOrLockTime)) {
373
+ tx = Transaction.fromBinary(txOrLockTime);
374
+ }
375
+ else {
376
+ tx = txOrLockTime;
377
+ }
378
+ // If all inputs have max sequence, the transaction is final regardless of lockTime
379
+ if (tx.inputs.every((i) => i.sequence === MAXINT)) {
380
+ return true;
381
+ }
382
+ nLockTime = tx.lockTime;
383
+ }
384
+ // If lockTime >= BLOCK_LIMIT, it's a timestamp (seconds since epoch)
385
+ if (nLockTime >= BLOCK_LIMIT) {
386
+ const currentTime = Math.floor(Date.now() / 1000);
387
+ return nLockTime < currentTime;
388
+ }
389
+ // Otherwise, it's a block height
390
+ const height = await this.getHeight();
391
+ return nLockTime < height;
230
392
  }
231
393
  }