@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,88 @@
1
+ /**
2
+ * Tokens Module
3
+ *
4
+ * Functions for managing BSV21 tokens.
5
+ */
6
+ import { type WalletInterface, type WalletOutput } from "@bsv/sdk";
7
+ import type { OneSatServices } from "../../services/OneSatServices";
8
+ export interface Bsv21Balance {
9
+ /** Token protocol (bsv-20) */
10
+ p: string;
11
+ /** Token ID (outpoint for BSV21, tick for BSV20) */
12
+ id: string;
13
+ /** Token symbol */
14
+ sym?: string;
15
+ /** Token icon URL */
16
+ icon?: string;
17
+ /** Decimal places */
18
+ dec: number;
19
+ /** Total amount (confirmed + pending) */
20
+ amt: string;
21
+ /** Breakdown of confirmed vs pending */
22
+ all: {
23
+ confirmed: bigint;
24
+ pending: bigint;
25
+ };
26
+ /** Listed amounts (if applicable) */
27
+ listed: {
28
+ confirmed: bigint;
29
+ pending: bigint;
30
+ };
31
+ }
32
+ export interface SendBsv21Request {
33
+ /** Token ID (txid_vout format) */
34
+ tokenId: string;
35
+ /** Destination address */
36
+ address: string;
37
+ /** Amount to send (as bigint or string) */
38
+ amount: bigint | string;
39
+ }
40
+ export interface TokenOperationResponse {
41
+ txid?: string;
42
+ rawtx?: string;
43
+ error?: string;
44
+ }
45
+ /**
46
+ * List BSV21 token outputs from the bsv21 basket.
47
+ * Returns WalletOutput[] directly - use tags for metadata.
48
+ */
49
+ export declare function listTokens(cwi: WalletInterface, limit?: number): Promise<WalletOutput[]>;
50
+ /**
51
+ * Get aggregated BSV21 token balances.
52
+ * Groups outputs by token ID and sums amounts.
53
+ */
54
+ export declare function getBsv21Balances(cwi: WalletInterface): Promise<Bsv21Balance[]>;
55
+ /**
56
+ * Send BSV21 tokens to an address.
57
+ *
58
+ * Flow:
59
+ * 1. Get token UTXOs from basket
60
+ * 2. Validate each with BSV21 service
61
+ * 3. Select UTXOs until we have enough tokens
62
+ * 4. Build transfer inscription outputs
63
+ * 5. Create and sign transaction
64
+ */
65
+ export declare function sendBsv21(cwi: WalletInterface, request: SendBsv21Request, services?: OneSatServices): Promise<TokenOperationResponse>;
66
+ export interface PurchaseBsv21Request {
67
+ /** Token ID (txid_vout format of the deploy transaction) */
68
+ tokenId: string;
69
+ /** Outpoint of listed token UTXO (OrdLock containing BSV21) */
70
+ outpoint: string;
71
+ /** Amount of tokens in the listing */
72
+ amount: bigint | string;
73
+ /** Optional marketplace fee address */
74
+ marketplaceAddress?: string;
75
+ /** Optional marketplace fee rate (0-1) */
76
+ marketplaceRate?: number;
77
+ }
78
+ /**
79
+ * Purchase BSV21 tokens from marketplace.
80
+ *
81
+ * Flow:
82
+ * 1. Fetch listing BEEF to get the locking script
83
+ * 2. Decode OrdLock to get price and payout
84
+ * 3. Build BSV21 transfer output for buyer
85
+ * 4. Build payment output for seller
86
+ * 5. Build custom OrdLock purchase unlock (preimage only, no signature)
87
+ */
88
+ export declare function purchaseBsv21(cwi: WalletInterface, request: PurchaseBsv21Request, services?: OneSatServices): Promise<TokenOperationResponse>;
@@ -0,0 +1,400 @@
1
+ /**
2
+ * Tokens Module
3
+ *
4
+ * Functions for managing BSV21 tokens.
5
+ */
6
+ import { BigNumber, LockingScript, OP, P2PKH, PublicKey, Transaction, TransactionSignature, UnlockingScript, Utils, } from "@bsv/sdk";
7
+ import { BSV21, OrdLock } from "@bopen-io/templates";
8
+ import { BSV21_BASKET } from "../constants";
9
+ /**
10
+ * List BSV21 token outputs from the bsv21 basket.
11
+ * Returns WalletOutput[] directly - use tags for metadata.
12
+ */
13
+ export async function listTokens(cwi, limit = 10000) {
14
+ const result = await cwi.listOutputs({
15
+ basket: BSV21_BASKET,
16
+ includeTags: true,
17
+ limit,
18
+ });
19
+ return result.outputs;
20
+ }
21
+ /**
22
+ * Get aggregated BSV21 token balances.
23
+ * Groups outputs by token ID and sums amounts.
24
+ */
25
+ export async function getBsv21Balances(cwi) {
26
+ const outputs = await listTokens(cwi);
27
+ const balanceMap = new Map();
28
+ for (const o of outputs) {
29
+ const idTag = o.tags?.find((t) => t.startsWith("id:"));
30
+ const amtTag = o.tags?.find((t) => t.startsWith("amt:"))?.slice(4);
31
+ if (!idTag || !amtTag)
32
+ continue;
33
+ const idContent = idTag.slice(3);
34
+ const lastColonIdx = idContent.lastIndexOf(":");
35
+ if (lastColonIdx === -1)
36
+ continue;
37
+ const tokenId = idContent.slice(0, lastColonIdx);
38
+ const status = idContent.slice(lastColonIdx + 1);
39
+ if (status === "invalid")
40
+ continue;
41
+ const isConfirmed = status === "valid";
42
+ const amt = BigInt(amtTag);
43
+ const dec = parseInt(o.tags?.find((t) => t.startsWith("dec:"))?.slice(4) || "0", 10);
44
+ const symTag = o.tags?.find((t) => t.startsWith("sym:"))?.slice(4);
45
+ const iconTag = o.tags?.find((t) => t.startsWith("icon:"))?.slice(5);
46
+ const existing = balanceMap.get(tokenId);
47
+ if (existing) {
48
+ if (isConfirmed)
49
+ existing.confirmed += amt;
50
+ else
51
+ existing.pending += amt;
52
+ }
53
+ else {
54
+ balanceMap.set(tokenId, {
55
+ id: tokenId,
56
+ confirmed: isConfirmed ? amt : 0n,
57
+ pending: isConfirmed ? 0n : amt,
58
+ sym: symTag,
59
+ icon: iconTag,
60
+ dec,
61
+ });
62
+ }
63
+ }
64
+ return Array.from(balanceMap.values()).map((b) => ({
65
+ p: "bsv-20",
66
+ op: "transfer",
67
+ dec: b.dec,
68
+ amt: (b.confirmed + b.pending).toString(),
69
+ id: b.id,
70
+ sym: b.sym,
71
+ icon: b.icon,
72
+ all: { confirmed: b.confirmed, pending: b.pending },
73
+ listed: { confirmed: 0n, pending: 0n },
74
+ }));
75
+ }
76
+ /**
77
+ * Send BSV21 tokens to an address.
78
+ *
79
+ * Flow:
80
+ * 1. Get token UTXOs from basket
81
+ * 2. Validate each with BSV21 service
82
+ * 3. Select UTXOs until we have enough tokens
83
+ * 4. Build transfer inscription outputs
84
+ * 5. Create and sign transaction
85
+ */
86
+ export async function sendBsv21(cwi, request, services) {
87
+ try {
88
+ const { tokenId, address, amount: rawAmount } = request;
89
+ const amount = typeof rawAmount === "string" ? BigInt(rawAmount) : rawAmount;
90
+ // Validate amount
91
+ if (amount <= 0n) {
92
+ return { error: "amount-must-be-positive" };
93
+ }
94
+ // Validate token ID format (txid_vout)
95
+ const parts = tokenId.split("_");
96
+ if (parts.length !== 2 || parts[0].length !== 64 || !/^\d+$/.test(parts[1])) {
97
+ return { error: "invalid-token-id-format" };
98
+ }
99
+ // Get token UTXOs from basket
100
+ const result = await cwi.listOutputs({
101
+ basket: BSV21_BASKET,
102
+ includeTags: true,
103
+ include: "locking scripts",
104
+ limit: 10000,
105
+ });
106
+ // Filter UTXOs matching tokenId (exclude invalid)
107
+ const tokenUtxos = result.outputs.filter((o) => {
108
+ const idTag = o.tags?.find((t) => t.startsWith("id:"));
109
+ if (!idTag)
110
+ return false;
111
+ const idContent = idTag.slice(3); // Remove "id:" prefix
112
+ const lastColonIdx = idContent.lastIndexOf(":");
113
+ if (lastColonIdx === -1)
114
+ return false;
115
+ const id = idContent.slice(0, lastColonIdx);
116
+ const status = idContent.slice(lastColonIdx + 1);
117
+ return id === tokenId && status !== "invalid";
118
+ });
119
+ if (tokenUtxos.length === 0) {
120
+ return { error: "no-token-utxos-found" };
121
+ }
122
+ // Validate UTXOs with BSV21 service and select valid ones
123
+ const selected = [];
124
+ let totalIn = 0n;
125
+ for (const utxo of tokenUtxos) {
126
+ if (totalIn >= amount)
127
+ break;
128
+ // Get amount from tag
129
+ const amtTag = utxo.tags?.find((t) => t.startsWith("amt:"));
130
+ if (!amtTag)
131
+ continue;
132
+ const utxoAmount = BigInt(amtTag.slice(4));
133
+ // Validate with BSV21 service if available
134
+ if (services?.bsv21) {
135
+ try {
136
+ const [txid] = utxo.outpoint.split("_");
137
+ const validation = await services.bsv21.getTokenByTxid(tokenId, txid);
138
+ // If the output is returned and exists in outputs, it's valid
139
+ const outputData = validation.outputs.find((o) => `${validation.txid}_${o.vout}` === utxo.outpoint);
140
+ if (!outputData) {
141
+ continue; // Skip if not found in response
142
+ }
143
+ }
144
+ catch {
145
+ // If 404 or any error, the overlay doesn't know about it - skip
146
+ continue;
147
+ }
148
+ }
149
+ selected.push(utxo);
150
+ totalIn += utxoAmount;
151
+ }
152
+ if (totalIn < amount) {
153
+ return { error: "insufficient-validated-tokens" };
154
+ }
155
+ // Build outputs
156
+ const outputs = [];
157
+ // Output 1: Token transfer to destination
158
+ const p2pkh = new P2PKH();
159
+ const destinationLockingScript = p2pkh.lock(address);
160
+ const transferScript = BSV21.transfer(tokenId, amount).lock(destinationLockingScript);
161
+ outputs.push({
162
+ lockingScript: transferScript.toHex(),
163
+ satoshis: 1,
164
+ outputDescription: `Send ${amount} tokens`,
165
+ });
166
+ // Output 2: Token change (if any)
167
+ const change = totalIn - amount;
168
+ if (change > 0n) {
169
+ // Derive change address
170
+ const keyID = `${tokenId}-change-${Date.now()}`;
171
+ const { publicKey } = await cwi.getPublicKey({
172
+ protocolID: [1, "bsv21"],
173
+ keyID,
174
+ counterparty: "self",
175
+ forSelf: true,
176
+ });
177
+ const changeAddress = PublicKey.fromString(publicKey).toAddress();
178
+ const changeLockingScript = p2pkh.lock(changeAddress);
179
+ const changeScript = BSV21.transfer(tokenId, change).lock(changeLockingScript);
180
+ outputs.push({
181
+ lockingScript: changeScript.toHex(),
182
+ satoshis: 1,
183
+ outputDescription: "Token change",
184
+ basket: BSV21_BASKET,
185
+ tags: [`id:${tokenId}:pending`, `amt:${change}`],
186
+ });
187
+ }
188
+ // Get token symbol for description
189
+ const symTag = tokenUtxos[0]?.tags?.find((t) => t.startsWith("sym:"));
190
+ const symbol = symTag ? symTag.slice(4) : tokenId.slice(0, 8);
191
+ // Create the transaction
192
+ const createResult = await cwi.createAction({
193
+ description: `Send ${amount} ${symbol}`,
194
+ inputs: selected.map((o) => ({
195
+ outpoint: o.outpoint,
196
+ inputDescription: "Token input",
197
+ })),
198
+ outputs,
199
+ options: { signAndProcess: false },
200
+ });
201
+ if ("error" in createResult && createResult.error) {
202
+ return { error: String(createResult.error) };
203
+ }
204
+ if (!createResult.signableTransaction) {
205
+ return { error: "no-signable-transaction" };
206
+ }
207
+ // Sign and broadcast - all inputs are P2PKH, wallet handles signing
208
+ const signResult = await cwi.signAction({
209
+ reference: createResult.signableTransaction.reference,
210
+ spends: {},
211
+ });
212
+ if ("error" in signResult) {
213
+ return { error: String(signResult.error) };
214
+ }
215
+ return {
216
+ txid: signResult.txid,
217
+ rawtx: signResult.tx ? Buffer.from(signResult.tx).toString("hex") : undefined,
218
+ };
219
+ }
220
+ catch (error) {
221
+ return { error: error instanceof Error ? error.message : "unknown-error" };
222
+ }
223
+ }
224
+ /**
225
+ * Build serialized transaction output (satoshis + script) for OrdLock unlock.
226
+ */
227
+ function buildSerializedOutput(satoshis, script) {
228
+ const writer = new Utils.Writer();
229
+ writer.writeUInt64LEBn(new BigNumber(satoshis));
230
+ writer.writeVarIntNum(script.length);
231
+ writer.write(script);
232
+ return writer.toArray();
233
+ }
234
+ /**
235
+ * Build OrdLock purchase unlocking script.
236
+ * The purchase path requires no signature - just preimage and output data.
237
+ */
238
+ async function buildPurchaseUnlockingScript(tx, inputIndex, sourceSatoshis, lockingScript) {
239
+ if (tx.outputs.length < 2) {
240
+ throw new Error("Malformed transaction: requires at least 2 outputs");
241
+ }
242
+ const script = new UnlockingScript()
243
+ .writeBin(buildSerializedOutput(tx.outputs[0].satoshis ?? 0, tx.outputs[0].lockingScript.toBinary()));
244
+ if (tx.outputs.length > 2) {
245
+ const writer = new Utils.Writer();
246
+ for (const output of tx.outputs.slice(2)) {
247
+ writer.write(buildSerializedOutput(output.satoshis ?? 0, output.lockingScript.toBinary()));
248
+ }
249
+ script.writeBin(writer.toArray());
250
+ }
251
+ else {
252
+ script.writeOpCode(OP.OP_0);
253
+ }
254
+ const input = tx.inputs[inputIndex];
255
+ const sourceTXID = input.sourceTXID ?? input.sourceTransaction?.id("hex");
256
+ if (!sourceTXID) {
257
+ throw new Error("sourceTXID is required");
258
+ }
259
+ const preimage = TransactionSignature.format({
260
+ sourceTXID,
261
+ sourceOutputIndex: input.sourceOutputIndex,
262
+ sourceSatoshis,
263
+ transactionVersion: tx.version,
264
+ otherInputs: [],
265
+ inputIndex,
266
+ outputs: tx.outputs,
267
+ inputSequence: input.sequence ?? 0xffffffff,
268
+ subscript: lockingScript,
269
+ lockTime: tx.lockTime,
270
+ scope: TransactionSignature.SIGHASH_ALL |
271
+ TransactionSignature.SIGHASH_ANYONECANPAY |
272
+ TransactionSignature.SIGHASH_FORKID
273
+ });
274
+ return script.writeBin(preimage).writeOpCode(OP.OP_0);
275
+ }
276
+ /**
277
+ * Purchase BSV21 tokens from marketplace.
278
+ *
279
+ * Flow:
280
+ * 1. Fetch listing BEEF to get the locking script
281
+ * 2. Decode OrdLock to get price and payout
282
+ * 3. Build BSV21 transfer output for buyer
283
+ * 4. Build payment output for seller
284
+ * 5. Build custom OrdLock purchase unlock (preimage only, no signature)
285
+ */
286
+ export async function purchaseBsv21(cwi, request, services) {
287
+ try {
288
+ const { tokenId, outpoint, amount: rawAmount, marketplaceAddress, marketplaceRate } = request;
289
+ const tokenAmount = typeof rawAmount === "string" ? BigInt(rawAmount) : rawAmount;
290
+ if (!services) {
291
+ return { error: "services-required-for-purchase" };
292
+ }
293
+ // Parse outpoint
294
+ const parts = outpoint.split("_");
295
+ if (parts.length !== 2) {
296
+ return { error: "invalid-outpoint-format" };
297
+ }
298
+ const [txid, voutStr] = parts;
299
+ const vout = parseInt(voutStr, 10);
300
+ // Fetch listing BEEF to get the locking script
301
+ const beef = await services.getBeefForTxid(txid);
302
+ const listingBeefTx = beef.findTxid(txid);
303
+ if (!listingBeefTx?.tx) {
304
+ return { error: "listing-transaction-not-found" };
305
+ }
306
+ const listingOutput = listingBeefTx.tx.outputs[vout];
307
+ if (!listingOutput) {
308
+ return { error: "listing-output-not-found" };
309
+ }
310
+ // Decode OrdLock from listing script
311
+ const ordLockData = OrdLock.decode(listingOutput.lockingScript);
312
+ if (!ordLockData) {
313
+ return { error: "not-an-ordlock-listing" };
314
+ }
315
+ // Derive our token receive address
316
+ const { publicKey } = await cwi.getPublicKey({
317
+ protocolID: [1, "bsv21"],
318
+ keyID: `${tokenId}-${outpoint}`,
319
+ counterparty: "self",
320
+ forSelf: true,
321
+ });
322
+ const ourTokenAddress = PublicKey.fromString(publicKey).toAddress();
323
+ // Build outputs
324
+ const outputs = [];
325
+ // Output 0: Token transfer to buyer (BSV21 inscription + P2PKH)
326
+ const p2pkh = new P2PKH();
327
+ const buyerLockingScript = p2pkh.lock(ourTokenAddress);
328
+ const transferScript = BSV21.transfer(tokenId, tokenAmount).lock(buyerLockingScript);
329
+ outputs.push({
330
+ lockingScript: transferScript.toHex(),
331
+ satoshis: 1,
332
+ outputDescription: "Purchased tokens",
333
+ basket: BSV21_BASKET,
334
+ tags: [`id:${tokenId}:pending`, `amt:${tokenAmount}`],
335
+ });
336
+ // Output 1: Payment to seller (from payout in OrdLock)
337
+ // Parse payout: 8-byte LE satoshis + varint script length + script
338
+ const payoutReader = new Utils.Reader(ordLockData.payout);
339
+ const payoutSatoshis = payoutReader.readUInt64LEBn().toNumber();
340
+ const payoutScriptLen = payoutReader.readVarIntNum();
341
+ const payoutScriptBin = payoutReader.read(payoutScriptLen);
342
+ const payoutLockingScript = LockingScript.fromBinary(payoutScriptBin);
343
+ outputs.push({
344
+ lockingScript: payoutLockingScript.toHex(),
345
+ satoshis: payoutSatoshis,
346
+ outputDescription: "Payment to seller",
347
+ });
348
+ // Output 2+ (optional): Marketplace fee
349
+ if (marketplaceAddress && marketplaceRate && marketplaceRate > 0) {
350
+ const marketFee = Math.ceil(payoutSatoshis * marketplaceRate);
351
+ if (marketFee > 0) {
352
+ outputs.push({
353
+ lockingScript: p2pkh.lock(marketplaceAddress).toHex(),
354
+ satoshis: marketFee,
355
+ outputDescription: "Marketplace fee",
356
+ });
357
+ }
358
+ }
359
+ // Create the transaction with signAndProcess: false
360
+ // The listing input needs custom unlocking script
361
+ const createResult = await cwi.createAction({
362
+ description: `Purchase ${tokenAmount} tokens for ${payoutSatoshis} sats`,
363
+ inputBEEF: beef.toBinary(),
364
+ inputs: [{
365
+ outpoint,
366
+ inputDescription: "Listed token",
367
+ unlockingScriptLength: 500, // Estimate for purchase unlock (preimage + outputs)
368
+ }],
369
+ outputs,
370
+ options: { signAndProcess: false },
371
+ });
372
+ if ("error" in createResult && createResult.error) {
373
+ return { error: String(createResult.error) };
374
+ }
375
+ if (!createResult.signableTransaction) {
376
+ return { error: "no-signable-transaction" };
377
+ }
378
+ // Parse the transaction to build purchase unlock
379
+ const tx = Transaction.fromBEEF(createResult.signableTransaction.tx);
380
+ // Build purchase unlocking script
381
+ const unlockingScript = await buildPurchaseUnlockingScript(tx, 0, listingOutput.satoshis ?? 1, listingOutput.lockingScript);
382
+ // Sign and broadcast
383
+ const signResult = await cwi.signAction({
384
+ reference: createResult.signableTransaction.reference,
385
+ spends: {
386
+ 0: { unlockingScript: unlockingScript.toHex() },
387
+ },
388
+ });
389
+ if ("error" in signResult) {
390
+ return { error: String(signResult.error) };
391
+ }
392
+ return {
393
+ txid: signResult.txid,
394
+ rawtx: signResult.tx ? Buffer.from(signResult.tx).toString("hex") : undefined,
395
+ };
396
+ }
397
+ catch (error) {
398
+ return { error: error instanceof Error ? error.message : "unknown-error" };
399
+ }
400
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * CWI Chrome Transport - For extension popup/options pages
3
+ * Uses chrome.runtime.sendMessage directly to service worker
4
+ */
5
+ import type { WalletInterface } from '@bsv/sdk';
6
+ /**
7
+ * Create a CWI for extension context (popup, options page).
8
+ * Uses chrome.runtime.sendMessage directly to communicate with service worker.
9
+ */
10
+ export declare const createChromeCWI: () => WalletInterface;
11
+ export declare const ChromeCWI: WalletInterface;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * CWI Chrome Transport - For extension popup/options pages
3
+ * Uses chrome.runtime.sendMessage directly to service worker
4
+ */
5
+ import { createCWI } from './factory.js';
6
+ /**
7
+ * chrome.runtime.sendMessage-based transport for extension context.
8
+ * Communicates directly with service worker without content script intermediary.
9
+ */
10
+ const chromeTransport = (action, params) => {
11
+ return new Promise((resolve, reject) => {
12
+ // Use originator at message level (BRC-100 standard)
13
+ // Format as chrome-extension://<id> to match the admin originator in initWallet.ts
14
+ const originator = `chrome-extension://${chrome.runtime?.id}`;
15
+ console.log('[ChromeCWI] Sending message:', { action, originator, paramsType: typeof params });
16
+ chrome.runtime.sendMessage({ action, params, originator }, (response) => {
17
+ if (chrome.runtime.lastError) {
18
+ console.error('[ChromeCWI] Runtime error:', chrome.runtime.lastError.message);
19
+ reject(new Error(chrome.runtime.lastError.message));
20
+ return;
21
+ }
22
+ if (response.success) {
23
+ console.log('[ChromeCWI] Success:', action);
24
+ resolve(response.data);
25
+ }
26
+ else {
27
+ console.error('[ChromeCWI] Failed:', action, response.error);
28
+ reject(new Error(response.error || 'Unknown error'));
29
+ }
30
+ });
31
+ });
32
+ };
33
+ /**
34
+ * Create a CWI for extension context (popup, options page).
35
+ * Uses chrome.runtime.sendMessage directly to communicate with service worker.
36
+ */
37
+ export const createChromeCWI = () => createCWI(chromeTransport);
38
+ // Default instance for convenience
39
+ export const ChromeCWI = createChromeCWI();
@@ -0,0 +1,11 @@
1
+ /**
2
+ * CWI Event Transport - For browser pages
3
+ * Uses CustomEvent pattern, forwarded by content script to service worker
4
+ */
5
+ import type { WalletInterface } from '@bsv/sdk';
6
+ /**
7
+ * Create a CWI for browser page context.
8
+ * Uses CustomEvent pattern - requires content script to forward to service worker.
9
+ */
10
+ export declare const createEventCWI: () => WalletInterface;
11
+ export declare const CWI: WalletInterface;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * CWI Event Transport - For browser pages
3
+ * Uses CustomEvent pattern, forwarded by content script to service worker
4
+ */
5
+ import { createCWI } from './factory.js';
6
+ // Event name for requests (listened by content script)
7
+ const YOURS_REQUEST = 'YoursRequest';
8
+ /**
9
+ * CustomEvent-based transport for browser page context.
10
+ * Content script listens for these events and forwards to service worker.
11
+ */
12
+ const eventTransport = (action, params) => {
13
+ return new Promise((resolve, reject) => {
14
+ const messageId = `${action}-${Date.now()}-${Math.random()}`;
15
+ const requestEvent = new CustomEvent(YOURS_REQUEST, {
16
+ detail: { messageId, type: action, params },
17
+ });
18
+ function onResponse(e) {
19
+ const responseEvent = e;
20
+ const { detail } = responseEvent;
21
+ if (detail.success) {
22
+ resolve(detail.data);
23
+ }
24
+ else {
25
+ reject(new Error(detail.error || 'Unknown error'));
26
+ }
27
+ }
28
+ self.addEventListener(messageId, onResponse, { once: true });
29
+ self.dispatchEvent(requestEvent);
30
+ });
31
+ };
32
+ /**
33
+ * Create a CWI for browser page context.
34
+ * Uses CustomEvent pattern - requires content script to forward to service worker.
35
+ */
36
+ export const createEventCWI = () => createCWI(eventTransport);
37
+ // Default instance for convenience
38
+ export const CWI = createEventCWI();
@@ -0,0 +1,14 @@
1
+ /**
2
+ * CWI Factory - Creates WalletInterface implementations with pluggable transport
3
+ */
4
+ import type { WalletInterface } from '@bsv/sdk';
5
+ import { CWIEventName } from './types.js';
6
+ /**
7
+ * Transport function signature - sends a message and returns response
8
+ */
9
+ export type CWITransport = <TResult>(action: CWIEventName, params: unknown) => Promise<TResult>;
10
+ /**
11
+ * Create a WalletInterface implementation using the provided transport.
12
+ * The transport handles the actual message passing (CustomEvent, chrome.runtime, etc.)
13
+ */
14
+ export declare const createCWI: (transport: CWITransport) => WalletInterface;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * CWI Factory - Creates WalletInterface implementations with pluggable transport
3
+ */
4
+ import { CWIEventName } from './types.js';
5
+ /**
6
+ * Create a WalletInterface implementation using the provided transport.
7
+ * The transport handles the actual message passing (CustomEvent, chrome.runtime, etc.)
8
+ */
9
+ export const createCWI = (transport) => ({
10
+ // Output Management
11
+ listOutputs: (args) => transport(CWIEventName.LIST_OUTPUTS, args),
12
+ relinquishOutput: (args) => transport(CWIEventName.RELINQUISH_OUTPUT, args),
13
+ // Action Management
14
+ createAction: (args) => transport(CWIEventName.CREATE_ACTION, args),
15
+ signAction: (args) => transport(CWIEventName.SIGN_ACTION, args),
16
+ abortAction: (args) => transport(CWIEventName.ABORT_ACTION, args),
17
+ listActions: (args) => transport(CWIEventName.LIST_ACTIONS, args),
18
+ internalizeAction: (args) => transport(CWIEventName.INTERNALIZE_ACTION, args),
19
+ // Key Operations
20
+ getPublicKey: (args) => transport(CWIEventName.GET_PUBLIC_KEY, args),
21
+ revealCounterpartyKeyLinkage: (args) => transport(CWIEventName.REVEAL_COUNTERPARTY_KEY_LINKAGE, args),
22
+ revealSpecificKeyLinkage: (args) => transport(CWIEventName.REVEAL_SPECIFIC_KEY_LINKAGE, args),
23
+ // Cryptographic Operations
24
+ encrypt: (args) => transport(CWIEventName.ENCRYPT, args),
25
+ decrypt: (args) => transport(CWIEventName.DECRYPT, args),
26
+ createHmac: (args) => transport(CWIEventName.CREATE_HMAC, args),
27
+ verifyHmac: (args) => transport(CWIEventName.VERIFY_HMAC, args),
28
+ createSignature: (args) => transport(CWIEventName.CREATE_SIGNATURE, args),
29
+ verifySignature: (args) => transport(CWIEventName.VERIFY_SIGNATURE, args),
30
+ // Certificate Operations
31
+ acquireCertificate: (args) => transport(CWIEventName.ACQUIRE_CERTIFICATE, args),
32
+ listCertificates: (args) => transport(CWIEventName.LIST_CERTIFICATES, args),
33
+ proveCertificate: (args) => transport(CWIEventName.PROVE_CERTIFICATE, args),
34
+ relinquishCertificate: (args) => transport(CWIEventName.RELINQUISH_CERTIFICATE, args),
35
+ discoverByIdentityKey: (args) => transport(CWIEventName.DISCOVER_BY_IDENTITY_KEY, args),
36
+ discoverByAttributes: (args) => transport(CWIEventName.DISCOVER_BY_ATTRIBUTES, args),
37
+ // Status & Info
38
+ isAuthenticated: (args) => transport(CWIEventName.IS_AUTHENTICATED, args),
39
+ waitForAuthentication: (args) => transport(CWIEventName.WAIT_FOR_AUTHENTICATION, args),
40
+ getHeight: (args) => transport(CWIEventName.GET_HEIGHT, args),
41
+ getHeaderForHeight: (args) => transport(CWIEventName.GET_HEADER_FOR_HEIGHT, args),
42
+ getNetwork: (args) => transport(CWIEventName.GET_NETWORK, args),
43
+ getVersion: (args) => transport(CWIEventName.GET_VERSION, args),
44
+ });
@@ -0,0 +1,11 @@
1
+ /**
2
+ * CWI (Chrome Wallet Interface) - BRC-100 WalletInterface implementations
3
+ *
4
+ * Two implementations for different contexts:
5
+ * - event.ts: For browser pages (uses CustomEvent, forwarded by content script)
6
+ * - chrome.ts: For extension popup/options (uses chrome.runtime.sendMessage directly)
7
+ */
8
+ export { CWIEventName, type CWIResponseDetail } from './types.js';
9
+ export { createCWI, type CWITransport } from './factory.js';
10
+ export { createEventCWI, CWI as EventCWI } from './event.js';
11
+ export { createChromeCWI, ChromeCWI } from './chrome.js';
@@ -0,0 +1,11 @@
1
+ /**
2
+ * CWI (Chrome Wallet Interface) - BRC-100 WalletInterface implementations
3
+ *
4
+ * Two implementations for different contexts:
5
+ * - event.ts: For browser pages (uses CustomEvent, forwarded by content script)
6
+ * - chrome.ts: For extension popup/options (uses chrome.runtime.sendMessage directly)
7
+ */
8
+ export { CWIEventName } from './types.js';
9
+ export { createCWI } from './factory.js';
10
+ export { createEventCWI, CWI as EventCWI } from './event.js';
11
+ export { createChromeCWI, ChromeCWI } from './chrome.js';