@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,566 @@
1
+ /**
2
+ * Ordinals Module
3
+ *
4
+ * Skills for managing ordinals/inscriptions.
5
+ * Returns WalletOutput[] directly from the SDK - no custom mapping needed.
6
+ */
7
+ import { BigNumber, Hash, LockingScript, OP, P2PKH, PublicKey, Script, Transaction, TransactionSignature, UnlockingScript, Utils, } from "@bsv/sdk";
8
+ import { OrdLock } from "@bopen-io/templates";
9
+ import { ORDINALS_BASKET, ORDLOCK_PREFIX, ORDLOCK_SUFFIX } from "../constants";
10
+ // ============================================================================
11
+ // Constants
12
+ // ============================================================================
13
+ const ORDINAL_PROTOCOL = [1, "ordinal"];
14
+ const ORDINAL_LISTING_PROTOCOL = [1, "ordinal listing"];
15
+ // ============================================================================
16
+ // Internal helpers
17
+ // ============================================================================
18
+ async function deriveCancelAddressInternal(ctx, outpoint) {
19
+ const result = await ctx.wallet.getPublicKey({
20
+ protocolID: ORDINAL_LISTING_PROTOCOL,
21
+ keyID: outpoint,
22
+ forSelf: true,
23
+ });
24
+ return PublicKey.fromString(result.publicKey).toAddress();
25
+ }
26
+ function buildOrdLockScript(ordAddress, payAddress, price) {
27
+ const cancelPkh = Utils.fromBase58Check(ordAddress).data;
28
+ const payPkh = Utils.fromBase58Check(payAddress).data;
29
+ const payoutScript = new P2PKH().lock(payPkh).toBinary();
30
+ const writer = new Utils.Writer();
31
+ writer.writeUInt64LEBn(new BigNumber(price));
32
+ writer.writeVarIntNum(payoutScript.length);
33
+ writer.write(payoutScript);
34
+ const payoutOutput = writer.toArray();
35
+ return new Script()
36
+ .writeScript(Script.fromHex(ORDLOCK_PREFIX))
37
+ .writeBin(cancelPkh)
38
+ .writeBin(payoutOutput)
39
+ .writeScript(Script.fromHex(ORDLOCK_SUFFIX));
40
+ }
41
+ function buildSerializedOutput(satoshis, script) {
42
+ const writer = new Utils.Writer();
43
+ writer.writeUInt64LEBn(new BigNumber(satoshis));
44
+ writer.writeVarIntNum(script.length);
45
+ writer.write(script);
46
+ return writer.toArray();
47
+ }
48
+ async function buildPurchaseUnlockingScript(tx, inputIndex, sourceSatoshis, lockingScript) {
49
+ if (tx.outputs.length < 2) {
50
+ throw new Error("Malformed transaction: requires at least 2 outputs");
51
+ }
52
+ const script = new UnlockingScript().writeBin(buildSerializedOutput(tx.outputs[0].satoshis ?? 0, tx.outputs[0].lockingScript.toBinary()));
53
+ if (tx.outputs.length > 2) {
54
+ const writer = new Utils.Writer();
55
+ for (const output of tx.outputs.slice(2)) {
56
+ writer.write(buildSerializedOutput(output.satoshis ?? 0, output.lockingScript.toBinary()));
57
+ }
58
+ script.writeBin(writer.toArray());
59
+ }
60
+ else {
61
+ script.writeOpCode(OP.OP_0);
62
+ }
63
+ const input = tx.inputs[inputIndex];
64
+ const sourceTXID = input.sourceTXID ?? input.sourceTransaction?.id("hex");
65
+ if (!sourceTXID) {
66
+ throw new Error("sourceTXID is required");
67
+ }
68
+ const preimage = TransactionSignature.format({
69
+ sourceTXID,
70
+ sourceOutputIndex: input.sourceOutputIndex,
71
+ sourceSatoshis,
72
+ transactionVersion: tx.version,
73
+ otherInputs: [],
74
+ inputIndex,
75
+ outputs: tx.outputs,
76
+ inputSequence: input.sequence ?? 0xffffffff,
77
+ subscript: lockingScript,
78
+ lockTime: tx.lockTime,
79
+ scope: TransactionSignature.SIGHASH_ALL |
80
+ TransactionSignature.SIGHASH_ANYONECANPAY |
81
+ TransactionSignature.SIGHASH_FORKID,
82
+ });
83
+ return script.writeBin(preimage).writeOpCode(OP.OP_0);
84
+ }
85
+ // ============================================================================
86
+ // Builder functions (utilities for advanced use)
87
+ // ============================================================================
88
+ /**
89
+ * Build CreateActionArgs for transferring an ordinal.
90
+ * Does NOT execute - returns params for createAction.
91
+ */
92
+ export async function buildTransferOrdinal(ctx, request) {
93
+ const { outpoint, counterparty, address, paymail } = request;
94
+ if (!counterparty && !address && !paymail) {
95
+ return { error: "must-provide-counterparty-address-or-paymail" };
96
+ }
97
+ let recipientAddress;
98
+ if (counterparty) {
99
+ const { publicKey } = await ctx.wallet.getPublicKey({
100
+ protocolID: ORDINAL_PROTOCOL,
101
+ keyID: outpoint,
102
+ counterparty,
103
+ forSelf: false,
104
+ });
105
+ recipientAddress = PublicKey.fromString(publicKey).toAddress();
106
+ }
107
+ else if (paymail) {
108
+ return { error: "paymail-not-yet-implemented" };
109
+ }
110
+ else {
111
+ recipientAddress = address;
112
+ }
113
+ const result = await ctx.wallet.listOutputs({
114
+ basket: ORDINALS_BASKET,
115
+ include: "locking scripts",
116
+ limit: 10000,
117
+ });
118
+ if (!result.outputs.find((o) => o.outpoint === outpoint)) {
119
+ return { error: "ordinal-not-found" };
120
+ }
121
+ return {
122
+ description: "Transfer ordinal",
123
+ inputs: [{ outpoint, inputDescription: "Ordinal to transfer" }],
124
+ outputs: [
125
+ {
126
+ lockingScript: new P2PKH().lock(recipientAddress).toHex(),
127
+ satoshis: 1,
128
+ outputDescription: "Ordinal transfer",
129
+ },
130
+ ],
131
+ };
132
+ }
133
+ /**
134
+ * Build CreateActionArgs for listing an ordinal for sale.
135
+ * Does NOT execute - returns params for createAction.
136
+ */
137
+ export async function buildListOrdinal(ctx, request) {
138
+ const { outpoint, price, payAddress } = request;
139
+ if (!payAddress)
140
+ return { error: "missing-pay-address" };
141
+ if (price <= 0)
142
+ return { error: "invalid-price" };
143
+ const result = await ctx.wallet.listOutputs({
144
+ basket: ORDINALS_BASKET,
145
+ includeTags: true,
146
+ include: "locking scripts",
147
+ limit: 10000,
148
+ });
149
+ const sourceOutput = result.outputs.find((o) => o.outpoint === outpoint);
150
+ if (!sourceOutput) {
151
+ return { error: "ordinal-not-found" };
152
+ }
153
+ const typeTag = sourceOutput.tags?.find((t) => t.startsWith("type:"));
154
+ const originTag = sourceOutput.tags?.find((t) => t.startsWith("origin:"));
155
+ const originOutpoint = originTag ? originTag.slice(7) : outpoint;
156
+ const cancelAddress = await deriveCancelAddressInternal(ctx, outpoint);
157
+ const lockingScript = buildOrdLockScript(cancelAddress, payAddress, price);
158
+ const tags = ["ordlock", `origin:${originOutpoint}`, `price:${price}`];
159
+ if (typeTag)
160
+ tags.push(typeTag);
161
+ return {
162
+ description: `List ordinal for ${price} sats`,
163
+ inputs: [{ outpoint, inputDescription: "Ordinal to list" }],
164
+ outputs: [
165
+ {
166
+ lockingScript: lockingScript.toHex(),
167
+ satoshis: 1,
168
+ outputDescription: `List ordinal for ${price} sats`,
169
+ basket: ORDINALS_BASKET,
170
+ tags,
171
+ customInstructions: JSON.stringify({
172
+ protocolID: ORDINAL_LISTING_PROTOCOL,
173
+ keyID: outpoint,
174
+ }),
175
+ },
176
+ ],
177
+ };
178
+ }
179
+ /**
180
+ * List ordinals from the wallet.
181
+ */
182
+ export const listOrdinals = {
183
+ meta: {
184
+ name: "listOrdinals",
185
+ description: "List ordinals/inscriptions from the wallet",
186
+ category: "ordinals",
187
+ inputSchema: {
188
+ type: "object",
189
+ properties: {
190
+ limit: { type: "integer", description: "Max number of ordinals to return (default: 100)" },
191
+ offset: { type: "integer", description: "Offset for pagination (default: 0)" },
192
+ },
193
+ },
194
+ },
195
+ async execute(ctx, input) {
196
+ const result = await ctx.wallet.listOutputs({
197
+ basket: ORDINALS_BASKET,
198
+ includeTags: true,
199
+ includeCustomInstructions: true,
200
+ limit: input.limit ?? 100,
201
+ offset: input.offset ?? 0,
202
+ });
203
+ return result.outputs;
204
+ },
205
+ };
206
+ /**
207
+ * Derive a cancel address for an ordinal listing.
208
+ */
209
+ export const deriveCancelAddress = {
210
+ meta: {
211
+ name: "deriveCancelAddress",
212
+ description: "Derive the cancel address for an ordinal listing",
213
+ category: "ordinals",
214
+ inputSchema: {
215
+ type: "object",
216
+ properties: {
217
+ outpoint: { type: "string", description: "Outpoint of the ordinal listing" },
218
+ },
219
+ required: ["outpoint"],
220
+ },
221
+ },
222
+ async execute(ctx, input) {
223
+ return deriveCancelAddressInternal(ctx, input.outpoint);
224
+ },
225
+ };
226
+ /**
227
+ * Transfer an ordinal to a new owner.
228
+ */
229
+ export const transferOrdinal = {
230
+ meta: {
231
+ name: "transferOrdinal",
232
+ description: "Transfer an ordinal to a new owner via counterparty pubkey, address, or paymail",
233
+ category: "ordinals",
234
+ inputSchema: {
235
+ type: "object",
236
+ properties: {
237
+ outpoint: { type: "string", description: "Outpoint of the ordinal (txid_vout format)" },
238
+ counterparty: { type: "string", description: "Recipient identity public key (hex)" },
239
+ address: { type: "string", description: "Recipient P2PKH address" },
240
+ paymail: { type: "string", description: "Recipient paymail address" },
241
+ },
242
+ required: ["outpoint"],
243
+ },
244
+ },
245
+ async execute(ctx, input) {
246
+ try {
247
+ const params = await buildTransferOrdinal(ctx, input);
248
+ if ("error" in params) {
249
+ return params;
250
+ }
251
+ const result = await ctx.wallet.createAction(params);
252
+ if (!result.txid) {
253
+ return { error: "no-txid-returned" };
254
+ }
255
+ return { txid: result.txid, rawtx: result.tx ? Utils.toHex(result.tx) : undefined };
256
+ }
257
+ catch (error) {
258
+ return { error: error instanceof Error ? error.message : "unknown-error" };
259
+ }
260
+ },
261
+ };
262
+ /**
263
+ * List an ordinal for sale on the global orderbook.
264
+ */
265
+ export const listOrdinal = {
266
+ meta: {
267
+ name: "listOrdinal",
268
+ description: "List an ordinal for sale on the global orderbook",
269
+ category: "ordinals",
270
+ inputSchema: {
271
+ type: "object",
272
+ properties: {
273
+ outpoint: { type: "string", description: "Outpoint of the ordinal to list" },
274
+ price: { type: "integer", description: "Price in satoshis" },
275
+ payAddress: { type: "string", description: "Address to receive payment on purchase" },
276
+ },
277
+ required: ["outpoint", "price", "payAddress"],
278
+ },
279
+ },
280
+ async execute(ctx, input) {
281
+ try {
282
+ const params = await buildListOrdinal(ctx, input);
283
+ if ("error" in params) {
284
+ return params;
285
+ }
286
+ const result = await ctx.wallet.createAction(params);
287
+ if (!result.txid) {
288
+ return { error: "no-txid-returned" };
289
+ }
290
+ return { txid: result.txid, rawtx: result.tx ? Utils.toHex(result.tx) : undefined };
291
+ }
292
+ catch (error) {
293
+ return { error: error instanceof Error ? error.message : "unknown-error" };
294
+ }
295
+ },
296
+ };
297
+ /**
298
+ * Cancel an ordinal listing.
299
+ */
300
+ export const cancelListing = {
301
+ meta: {
302
+ name: "cancelListing",
303
+ description: "Cancel an ordinal listing and return the ordinal to the wallet",
304
+ category: "ordinals",
305
+ inputSchema: {
306
+ type: "object",
307
+ properties: {
308
+ outpoint: { type: "string", description: "Outpoint of the listing to cancel" },
309
+ },
310
+ required: ["outpoint"],
311
+ },
312
+ },
313
+ async execute(ctx, input) {
314
+ try {
315
+ const { outpoint } = input;
316
+ const result = await ctx.wallet.listOutputs({
317
+ basket: ORDINALS_BASKET,
318
+ includeTags: true,
319
+ includeCustomInstructions: true,
320
+ include: "locking scripts",
321
+ limit: 10000,
322
+ });
323
+ const listing = result.outputs.find((o) => o.outpoint === outpoint);
324
+ if (!listing) {
325
+ return { error: "listing-not-found" };
326
+ }
327
+ if (!listing.customInstructions) {
328
+ return { error: "missing-custom-instructions" };
329
+ }
330
+ const { protocolID, keyID } = JSON.parse(listing.customInstructions);
331
+ const typeTag = listing.tags?.find((t) => t.startsWith("type:"));
332
+ const originTag = listing.tags?.find((t) => t.startsWith("origin:"));
333
+ const cancelAddress = await deriveCancelAddressInternal(ctx, keyID);
334
+ const tags = [];
335
+ if (typeTag)
336
+ tags.push(typeTag);
337
+ if (originTag)
338
+ tags.push(originTag);
339
+ const createResult = await ctx.wallet.createAction({
340
+ description: "Cancel ordinal listing",
341
+ inputs: [
342
+ {
343
+ outpoint,
344
+ inputDescription: "Listed ordinal",
345
+ unlockingScriptLength: 108,
346
+ },
347
+ ],
348
+ outputs: [
349
+ {
350
+ lockingScript: new P2PKH().lock(cancelAddress).toHex(),
351
+ satoshis: 1,
352
+ outputDescription: "Cancelled listing",
353
+ basket: ORDINALS_BASKET,
354
+ tags,
355
+ customInstructions: JSON.stringify({ protocolID, keyID }),
356
+ },
357
+ ],
358
+ options: { signAndProcess: false },
359
+ });
360
+ if ("error" in createResult && createResult.error) {
361
+ return { error: String(createResult.error) };
362
+ }
363
+ if (!createResult.signableTransaction) {
364
+ return { error: "no-signable-transaction" };
365
+ }
366
+ const tx = Transaction.fromBEEF(createResult.signableTransaction.tx);
367
+ const txInput = tx.inputs[0];
368
+ const lockingScript = Script.fromHex(listing.lockingScript);
369
+ const sourceTXID = txInput.sourceTXID ?? txInput.sourceTransaction?.id("hex");
370
+ if (!sourceTXID) {
371
+ return { error: "missing-source-txid" };
372
+ }
373
+ const preimage = TransactionSignature.format({
374
+ sourceTXID,
375
+ sourceOutputIndex: txInput.sourceOutputIndex,
376
+ sourceSatoshis: listing.satoshis,
377
+ transactionVersion: tx.version,
378
+ otherInputs: [],
379
+ inputIndex: 0,
380
+ outputs: tx.outputs,
381
+ inputSequence: txInput.sequence ?? 0xffffffff,
382
+ subscript: lockingScript,
383
+ lockTime: tx.lockTime,
384
+ scope: TransactionSignature.SIGHASH_ALL |
385
+ TransactionSignature.SIGHASH_ANYONECANPAY |
386
+ TransactionSignature.SIGHASH_FORKID,
387
+ });
388
+ const sighash = Hash.sha256(Hash.sha256(preimage));
389
+ const { signature } = await ctx.wallet.createSignature({
390
+ protocolID,
391
+ keyID,
392
+ counterparty: "self",
393
+ hashToDirectlySign: Array.from(sighash),
394
+ });
395
+ const { publicKey } = await ctx.wallet.getPublicKey({
396
+ protocolID,
397
+ keyID,
398
+ forSelf: true,
399
+ });
400
+ const unlockingScript = new UnlockingScript()
401
+ .writeBin(signature)
402
+ .writeBin(Utils.toArray(publicKey, "hex"))
403
+ .writeOpCode(OP.OP_1);
404
+ const signResult = await ctx.wallet.signAction({
405
+ reference: createResult.signableTransaction.reference,
406
+ spends: {
407
+ 0: { unlockingScript: unlockingScript.toHex() },
408
+ },
409
+ });
410
+ if ("error" in signResult) {
411
+ return { error: String(signResult.error) };
412
+ }
413
+ return {
414
+ txid: signResult.txid,
415
+ rawtx: signResult.tx ? Utils.toHex(signResult.tx) : undefined,
416
+ };
417
+ }
418
+ catch (error) {
419
+ return { error: error instanceof Error ? error.message : "unknown-error" };
420
+ }
421
+ },
422
+ };
423
+ /**
424
+ * Purchase an ordinal from the global orderbook.
425
+ */
426
+ export const purchaseOrdinal = {
427
+ meta: {
428
+ name: "purchaseOrdinal",
429
+ description: "Purchase an ordinal from the global orderbook",
430
+ category: "ordinals",
431
+ requiresServices: true,
432
+ inputSchema: {
433
+ type: "object",
434
+ properties: {
435
+ outpoint: { type: "string", description: "Outpoint of the listing to purchase" },
436
+ marketplaceAddress: { type: "string", description: "Marketplace address for fees" },
437
+ marketplaceRate: { type: "number", description: "Marketplace fee rate (0-1)" },
438
+ contentType: { type: "string", description: "Content type (auto-detected if not provided)" },
439
+ origin: { type: "string", description: "Origin outpoint (auto-detected if not provided)" },
440
+ },
441
+ required: ["outpoint"],
442
+ },
443
+ },
444
+ async execute(ctx, input) {
445
+ try {
446
+ const { outpoint, marketplaceAddress, marketplaceRate } = input;
447
+ if (!ctx.services) {
448
+ return { error: "services-required-for-purchase" };
449
+ }
450
+ const parts = outpoint.split("_");
451
+ if (parts.length !== 2) {
452
+ return { error: "invalid-outpoint-format" };
453
+ }
454
+ const [txid, voutStr] = parts;
455
+ const vout = Number.parseInt(voutStr, 10);
456
+ let { contentType, origin } = input;
457
+ if (!contentType || !origin) {
458
+ const metadata = await ctx.services.ordfs.getMetadata(outpoint);
459
+ contentType = contentType ?? metadata.contentType;
460
+ origin = origin ?? metadata.origin ?? outpoint;
461
+ }
462
+ const beef = await ctx.services.getBeefForTxid(txid);
463
+ const listingBeefTx = beef.findTxid(txid);
464
+ if (!listingBeefTx?.tx) {
465
+ return { error: "listing-transaction-not-found" };
466
+ }
467
+ const listingOutput = listingBeefTx.tx.outputs[vout];
468
+ if (!listingOutput) {
469
+ return { error: "listing-output-not-found" };
470
+ }
471
+ const ordLockData = OrdLock.decode(listingOutput.lockingScript);
472
+ if (!ordLockData) {
473
+ return { error: "not-an-ordlock-listing" };
474
+ }
475
+ const { publicKey } = await ctx.wallet.getPublicKey({
476
+ protocolID: ORDINAL_PROTOCOL,
477
+ keyID: outpoint,
478
+ counterparty: "self",
479
+ forSelf: true,
480
+ });
481
+ const ourOrdAddress = PublicKey.fromString(publicKey).toAddress();
482
+ const outputs = [];
483
+ const p2pkh = new P2PKH();
484
+ outputs.push({
485
+ lockingScript: p2pkh.lock(ourOrdAddress).toHex(),
486
+ satoshis: 1,
487
+ outputDescription: "Purchased ordinal",
488
+ basket: ORDINALS_BASKET,
489
+ tags: [`type:${contentType}`, `origin:${origin}`],
490
+ customInstructions: JSON.stringify({
491
+ protocolID: ORDINAL_PROTOCOL,
492
+ keyID: outpoint,
493
+ }),
494
+ });
495
+ const payoutReader = new Utils.Reader(ordLockData.payout);
496
+ const payoutSatoshis = payoutReader.readUInt64LEBn().toNumber();
497
+ const payoutScriptLen = payoutReader.readVarIntNum();
498
+ const payoutScriptBin = payoutReader.read(payoutScriptLen);
499
+ const payoutLockingScript = LockingScript.fromBinary(payoutScriptBin);
500
+ outputs.push({
501
+ lockingScript: payoutLockingScript.toHex(),
502
+ satoshis: payoutSatoshis,
503
+ outputDescription: "Payment to seller",
504
+ });
505
+ if (marketplaceAddress && marketplaceRate && marketplaceRate > 0) {
506
+ const marketFee = Math.ceil(payoutSatoshis * marketplaceRate);
507
+ if (marketFee > 0) {
508
+ outputs.push({
509
+ lockingScript: p2pkh.lock(marketplaceAddress).toHex(),
510
+ satoshis: marketFee,
511
+ outputDescription: "Marketplace fee",
512
+ });
513
+ }
514
+ }
515
+ const createResult = await ctx.wallet.createAction({
516
+ description: `Purchase ordinal for ${payoutSatoshis} sats`,
517
+ inputBEEF: beef.toBinary(),
518
+ inputs: [
519
+ {
520
+ outpoint,
521
+ inputDescription: "Listed ordinal",
522
+ unlockingScriptLength: 500,
523
+ },
524
+ ],
525
+ outputs,
526
+ options: { signAndProcess: false },
527
+ });
528
+ if ("error" in createResult && createResult.error) {
529
+ return { error: String(createResult.error) };
530
+ }
531
+ if (!createResult.signableTransaction) {
532
+ return { error: "no-signable-transaction" };
533
+ }
534
+ const tx = Transaction.fromBEEF(createResult.signableTransaction.tx);
535
+ const unlockingScript = await buildPurchaseUnlockingScript(tx, 0, listingOutput.satoshis ?? 1, listingOutput.lockingScript);
536
+ const signResult = await ctx.wallet.signAction({
537
+ reference: createResult.signableTransaction.reference,
538
+ spends: {
539
+ 0: { unlockingScript: unlockingScript.toHex() },
540
+ },
541
+ });
542
+ if ("error" in signResult) {
543
+ return { error: String(signResult.error) };
544
+ }
545
+ return {
546
+ txid: signResult.txid,
547
+ rawtx: signResult.tx ? Utils.toHex(signResult.tx) : undefined,
548
+ };
549
+ }
550
+ catch (error) {
551
+ return { error: error instanceof Error ? error.message : "unknown-error" };
552
+ }
553
+ },
554
+ };
555
+ // ============================================================================
556
+ // Module exports
557
+ // ============================================================================
558
+ /** All ordinals skills for registry */
559
+ export const ordinalsSkills = [
560
+ listOrdinals,
561
+ deriveCancelAddress,
562
+ transferOrdinal,
563
+ listOrdinal,
564
+ cancelListing,
565
+ purchaseOrdinal,
566
+ ];
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Payments Module
3
+ *
4
+ * Skills for sending BSV payments.
5
+ */
6
+ import type { Skill } from "../skills/types";
7
+ export interface SendBsvRequest {
8
+ /** Destination address (P2PKH) */
9
+ address?: string;
10
+ /** Destination paymail */
11
+ paymail?: string;
12
+ /** Amount in satoshis */
13
+ satoshis: number;
14
+ /** Custom locking script (hex) */
15
+ script?: string;
16
+ /** OP_RETURN data */
17
+ data?: string[];
18
+ /** Inscription data */
19
+ inscription?: {
20
+ base64Data: string;
21
+ mimeType: string;
22
+ map?: Record<string, string>;
23
+ };
24
+ }
25
+ export interface SendBsvResponse {
26
+ txid?: string;
27
+ rawtx?: string;
28
+ error?: string;
29
+ }
30
+ /** Input for sendBsv skill */
31
+ export interface SendBsvInput {
32
+ requests: SendBsvRequest[];
33
+ }
34
+ /**
35
+ * Send BSV to one or more destinations.
36
+ */
37
+ export declare const sendBsv: Skill<SendBsvInput, SendBsvResponse>;
38
+ /** Input for sendAllBsv skill */
39
+ export interface SendAllBsvInput {
40
+ /** Destination address to send all funds to */
41
+ destination: string;
42
+ }
43
+ /**
44
+ * Send all BSV to a destination address.
45
+ */
46
+ export declare const sendAllBsv: Skill<SendAllBsvInput, SendBsvResponse>;
47
+ /** All payment skills for registry */
48
+ export declare const paymentsSkills: (Skill<SendBsvInput, SendBsvResponse> | Skill<SendAllBsvInput, SendBsvResponse>)[];