@1sat/wallet-toolbox 0.0.9 → 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.
- package/dist/api/balance/index.d.ts +20 -8
- package/dist/api/balance/index.js +104 -51
- package/dist/api/broadcast/index.d.ts +5 -3
- package/dist/api/broadcast/index.js +65 -37
- package/dist/api/index.d.ts +15 -15
- package/dist/api/index.js +38 -17
- package/dist/api/inscriptions/index.d.ts +9 -9
- package/dist/api/inscriptions/index.js +79 -31
- package/dist/api/locks/index.d.ts +15 -12
- package/dist/api/locks/index.js +252 -194
- package/dist/api/ordinals/index.d.ts +50 -35
- package/dist/api/ordinals/index.js +469 -349
- package/dist/api/payments/index.d.ts +15 -4
- package/dist/api/payments/index.js +147 -92
- package/dist/api/signing/index.d.ts +8 -5
- package/dist/api/signing/index.js +70 -33
- package/dist/api/skills/registry.d.ts +61 -0
- package/dist/api/skills/registry.js +74 -0
- package/dist/api/skills/types.d.ts +71 -0
- package/dist/api/skills/types.js +14 -0
- package/dist/api/tokens/index.d.ts +37 -38
- package/dist/api/tokens/index.js +398 -341
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/wallet/factory.d.ts +64 -0
- package/dist/wallet/factory.js +129 -0
- package/dist/wallet/index.d.ts +1 -0
- package/dist/wallet/index.js +1 -0
- package/package.json +10 -1
- package/dist/OneSatWallet.d.ts +0 -316
- package/dist/OneSatWallet.js +0 -956
- package/dist/api/OneSatApi.d.ts +0 -100
- package/dist/api/OneSatApi.js +0 -156
- package/dist/indexers/TransactionParser.d.ts +0 -53
|
@@ -1,36 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Ordinals Module
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Skills for managing ordinals/inscriptions.
|
|
5
5
|
* Returns WalletOutput[] directly from the SDK - no custom mapping needed.
|
|
6
6
|
*/
|
|
7
7
|
import { BigNumber, Hash, LockingScript, OP, P2PKH, PublicKey, Script, Transaction, TransactionSignature, UnlockingScript, Utils, } from "@bsv/sdk";
|
|
8
8
|
import { OrdLock } from "@bopen-io/templates";
|
|
9
9
|
import { ORDINALS_BASKET, ORDLOCK_PREFIX, ORDLOCK_SUFFIX } from "../constants";
|
|
10
|
-
//
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// Constants
|
|
12
|
+
// ============================================================================
|
|
13
|
+
const ORDINAL_PROTOCOL = [1, "ordinal"];
|
|
11
14
|
const ORDINAL_LISTING_PROTOCOL = [1, "ordinal listing"];
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
function
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Derive a cancel address for an ordinal listing.
|
|
20
|
-
* Uses the outpoint as keyID with security level 1 (self-only).
|
|
21
|
-
*/
|
|
22
|
-
export async function deriveCancelAddress(cwi, outpoint) {
|
|
23
|
-
const result = await cwi.getPublicKey({
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Internal helpers
|
|
17
|
+
// ============================================================================
|
|
18
|
+
async function deriveCancelAddressInternal(ctx, outpoint) {
|
|
19
|
+
const result = await ctx.wallet.getPublicKey({
|
|
24
20
|
protocolID: ORDINAL_LISTING_PROTOCOL,
|
|
25
21
|
keyID: outpoint,
|
|
26
22
|
forSelf: true,
|
|
27
23
|
});
|
|
28
|
-
|
|
29
|
-
return publicKey.toAddress();
|
|
24
|
+
return PublicKey.fromString(result.publicKey).toAddress();
|
|
30
25
|
}
|
|
31
|
-
/**
|
|
32
|
-
* Build OrdLock script for listing an ordinal.
|
|
33
|
-
*/
|
|
34
26
|
function buildOrdLockScript(ordAddress, payAddress, price) {
|
|
35
27
|
const cancelPkh = Utils.fromBase58Check(ordAddress).data;
|
|
36
28
|
const payPkh = Utils.fromBase58Check(payAddress).data;
|
|
@@ -46,31 +38,79 @@ function buildOrdLockScript(ordAddress, payAddress, price) {
|
|
|
46
38
|
.writeBin(payoutOutput)
|
|
47
39
|
.writeScript(Script.fromHex(ORDLOCK_SUFFIX));
|
|
48
40
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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,
|
|
61
82
|
});
|
|
62
|
-
return
|
|
83
|
+
return script.writeBin(preimage).writeOpCode(OP.OP_0);
|
|
63
84
|
}
|
|
85
|
+
// ============================================================================
|
|
86
|
+
// Builder functions (utilities for advanced use)
|
|
87
|
+
// ============================================================================
|
|
64
88
|
/**
|
|
65
89
|
* Build CreateActionArgs for transferring an ordinal.
|
|
66
90
|
* Does NOT execute - returns params for createAction.
|
|
67
91
|
*/
|
|
68
|
-
export async function buildTransferOrdinal(
|
|
69
|
-
const { outpoint,
|
|
70
|
-
if (
|
|
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) {
|
|
71
108
|
return { error: "paymail-not-yet-implemented" };
|
|
72
109
|
}
|
|
73
|
-
|
|
110
|
+
else {
|
|
111
|
+
recipientAddress = address;
|
|
112
|
+
}
|
|
113
|
+
const result = await ctx.wallet.listOutputs({
|
|
74
114
|
basket: ORDINALS_BASKET,
|
|
75
115
|
include: "locking scripts",
|
|
76
116
|
limit: 10000,
|
|
@@ -81,366 +121,446 @@ export async function buildTransferOrdinal(cwi, request) {
|
|
|
81
121
|
return {
|
|
82
122
|
description: "Transfer ordinal",
|
|
83
123
|
inputs: [{ outpoint, inputDescription: "Ordinal to transfer" }],
|
|
84
|
-
outputs: [
|
|
85
|
-
|
|
124
|
+
outputs: [
|
|
125
|
+
{
|
|
126
|
+
lockingScript: new P2PKH().lock(recipientAddress).toHex(),
|
|
86
127
|
satoshis: 1,
|
|
87
128
|
outputDescription: "Ordinal transfer",
|
|
88
|
-
}
|
|
129
|
+
},
|
|
130
|
+
],
|
|
89
131
|
};
|
|
90
132
|
}
|
|
91
133
|
/**
|
|
92
134
|
* Build CreateActionArgs for listing an ordinal for sale.
|
|
93
135
|
* Does NOT execute - returns params for createAction.
|
|
94
|
-
* If cancelAddress is not provided, it will be derived from the CWI.
|
|
95
136
|
*/
|
|
96
|
-
export async function buildListOrdinal(
|
|
137
|
+
export async function buildListOrdinal(ctx, request) {
|
|
97
138
|
const { outpoint, price, payAddress } = request;
|
|
98
139
|
if (!payAddress)
|
|
99
140
|
return { error: "missing-pay-address" };
|
|
100
141
|
if (price <= 0)
|
|
101
142
|
return { error: "invalid-price" };
|
|
102
|
-
const result = await
|
|
143
|
+
const result = await ctx.wallet.listOutputs({
|
|
103
144
|
basket: ORDINALS_BASKET,
|
|
145
|
+
includeTags: true,
|
|
104
146
|
include: "locking scripts",
|
|
105
147
|
limit: 10000,
|
|
106
148
|
});
|
|
107
|
-
|
|
149
|
+
const sourceOutput = result.outputs.find((o) => o.outpoint === outpoint);
|
|
150
|
+
if (!sourceOutput) {
|
|
108
151
|
return { error: "ordinal-not-found" };
|
|
109
152
|
}
|
|
110
|
-
|
|
111
|
-
const
|
|
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);
|
|
112
157
|
const lockingScript = buildOrdLockScript(cancelAddress, payAddress, price);
|
|
158
|
+
const tags = ["ordlock", `origin:${originOutpoint}`, `price:${price}`];
|
|
159
|
+
if (typeTag)
|
|
160
|
+
tags.push(typeTag);
|
|
113
161
|
return {
|
|
114
162
|
description: `List ordinal for ${price} sats`,
|
|
115
163
|
inputs: [{ outpoint, inputDescription: "Ordinal to list" }],
|
|
116
|
-
outputs: [
|
|
164
|
+
outputs: [
|
|
165
|
+
{
|
|
117
166
|
lockingScript: lockingScript.toHex(),
|
|
118
167
|
satoshis: 1,
|
|
119
168
|
outputDescription: `List ordinal for ${price} sats`,
|
|
120
169
|
basket: ORDINALS_BASKET,
|
|
121
|
-
tags
|
|
122
|
-
|
|
170
|
+
tags,
|
|
171
|
+
customInstructions: JSON.stringify({
|
|
172
|
+
protocolID: ORDINAL_LISTING_PROTOCOL,
|
|
173
|
+
keyID: outpoint,
|
|
174
|
+
}),
|
|
175
|
+
},
|
|
176
|
+
],
|
|
123
177
|
};
|
|
124
178
|
}
|
|
125
179
|
/**
|
|
126
|
-
*
|
|
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.
|
|
127
228
|
*/
|
|
128
|
-
export
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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 };
|
|
133
256
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
return { error: "no-txid-returned" };
|
|
257
|
+
catch (error) {
|
|
258
|
+
return { error: error instanceof Error ? error.message : "unknown-error" };
|
|
137
259
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
catch (error) {
|
|
141
|
-
return { error: error instanceof Error ? error.message : "unknown-error" };
|
|
142
|
-
}
|
|
143
|
-
}
|
|
260
|
+
},
|
|
261
|
+
};
|
|
144
262
|
/**
|
|
145
263
|
* List an ordinal for sale on the global orderbook.
|
|
146
264
|
*/
|
|
147
|
-
export
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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 };
|
|
152
291
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
return { error: "no-txid-returned" };
|
|
292
|
+
catch (error) {
|
|
293
|
+
return { error: error instanceof Error ? error.message : "unknown-error" };
|
|
156
294
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
catch (error) {
|
|
160
|
-
return { error: error instanceof Error ? error.message : "unknown-error" };
|
|
161
|
-
}
|
|
162
|
-
}
|
|
295
|
+
},
|
|
296
|
+
};
|
|
163
297
|
/**
|
|
164
298
|
* Cancel an ordinal listing.
|
|
165
|
-
* Uses the origin tag to recover the keyID for signing.
|
|
166
|
-
* Cancel unlock script: <sig> <pubkey> OP_1
|
|
167
299
|
*/
|
|
168
|
-
export
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
const listing = result.outputs.find((o) => o.outpoint === outpoint);
|
|
178
|
-
if (!listing) {
|
|
179
|
-
return { error: "listing-not-found" };
|
|
180
|
-
}
|
|
181
|
-
// Get the origin tag to recover the keyID
|
|
182
|
-
const originTag = listing.tags?.find((t) => t.startsWith("origin:"));
|
|
183
|
-
if (!originTag) {
|
|
184
|
-
return { error: "missing-origin-tag" };
|
|
185
|
-
}
|
|
186
|
-
const originOutpoint = originTag.slice(7); // Remove "origin:" prefix
|
|
187
|
-
// Derive the cancel address to get output destination
|
|
188
|
-
const cancelAddress = await deriveCancelAddress(cwi, originOutpoint);
|
|
189
|
-
// Create transaction with signAndProcess: false
|
|
190
|
-
const createResult = await cwi.createAction({
|
|
191
|
-
description: "Cancel ordinal listing",
|
|
192
|
-
inputs: [{
|
|
193
|
-
outpoint,
|
|
194
|
-
inputDescription: "Listed ordinal",
|
|
195
|
-
unlockingScriptLength: 108, // sig (73) + pubkey (34) + OP_1 (1)
|
|
196
|
-
}],
|
|
197
|
-
outputs: [{
|
|
198
|
-
lockingScript: new P2PKH().lock(cancelAddress).toHex(),
|
|
199
|
-
satoshis: 1,
|
|
200
|
-
outputDescription: "Cancelled listing",
|
|
201
|
-
basket: ORDINALS_BASKET,
|
|
202
|
-
}],
|
|
203
|
-
options: { signAndProcess: false },
|
|
204
|
-
});
|
|
205
|
-
if ("error" in createResult && createResult.error) {
|
|
206
|
-
return { error: String(createResult.error) };
|
|
207
|
-
}
|
|
208
|
-
if (!createResult.signableTransaction) {
|
|
209
|
-
return { error: "no-signable-transaction" };
|
|
210
|
-
}
|
|
211
|
-
// Parse transaction for signing
|
|
212
|
-
const tx = Transaction.fromBEEF(createResult.signableTransaction.tx);
|
|
213
|
-
const input = tx.inputs[0];
|
|
214
|
-
const lockingScript = Script.fromHex(listing.lockingScript);
|
|
215
|
-
// Build preimage for signature
|
|
216
|
-
const sourceTXID = input.sourceTXID ?? input.sourceTransaction?.id("hex");
|
|
217
|
-
if (!sourceTXID) {
|
|
218
|
-
return { error: "missing-source-txid" };
|
|
219
|
-
}
|
|
220
|
-
const preimage = TransactionSignature.format({
|
|
221
|
-
sourceTXID,
|
|
222
|
-
sourceOutputIndex: input.sourceOutputIndex,
|
|
223
|
-
sourceSatoshis: listing.satoshis,
|
|
224
|
-
transactionVersion: tx.version,
|
|
225
|
-
otherInputs: [],
|
|
226
|
-
inputIndex: 0,
|
|
227
|
-
outputs: tx.outputs,
|
|
228
|
-
inputSequence: input.sequence ?? 0xffffffff,
|
|
229
|
-
subscript: lockingScript,
|
|
230
|
-
lockTime: tx.lockTime,
|
|
231
|
-
scope: TransactionSignature.SIGHASH_ALL |
|
|
232
|
-
TransactionSignature.SIGHASH_ANYONECANPAY |
|
|
233
|
-
TransactionSignature.SIGHASH_FORKID,
|
|
234
|
-
});
|
|
235
|
-
// Hash preimage for signing
|
|
236
|
-
const sighash = Hash.sha256(Hash.sha256(preimage));
|
|
237
|
-
// Get signature via createSignature using origin outpoint as keyID
|
|
238
|
-
const { signature } = await cwi.createSignature({
|
|
239
|
-
protocolID: ORDINAL_LISTING_PROTOCOL,
|
|
240
|
-
keyID: originOutpoint,
|
|
241
|
-
counterparty: "self",
|
|
242
|
-
hashToDirectlySign: Array.from(sighash),
|
|
243
|
-
});
|
|
244
|
-
// Get public key
|
|
245
|
-
const { publicKey } = await cwi.getPublicKey({
|
|
246
|
-
protocolID: ORDINAL_LISTING_PROTOCOL,
|
|
247
|
-
keyID: originOutpoint,
|
|
248
|
-
forSelf: true,
|
|
249
|
-
});
|
|
250
|
-
// Build cancel unlocking script: <sig> <pubkey> OP_1
|
|
251
|
-
const unlockingScript = new UnlockingScript()
|
|
252
|
-
.writeBin(signature)
|
|
253
|
-
.writeBin(Utils.toArray(publicKey, "hex"))
|
|
254
|
-
.writeOpCode(OP.OP_1);
|
|
255
|
-
// Sign and broadcast
|
|
256
|
-
const signResult = await cwi.signAction({
|
|
257
|
-
reference: createResult.signableTransaction.reference,
|
|
258
|
-
spends: {
|
|
259
|
-
0: { unlockingScript: unlockingScript.toHex() },
|
|
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" },
|
|
260
309
|
},
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
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
|
+
};
|
|
264
417
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
rawtx: signResult.tx ? Utils.toHex(signResult.tx) : undefined,
|
|
268
|
-
};
|
|
269
|
-
}
|
|
270
|
-
catch (error) {
|
|
271
|
-
return { error: error instanceof Error ? error.message : "unknown-error" };
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
/**
|
|
275
|
-
* Build serialized transaction output (satoshis + script) for OrdLock unlock.
|
|
276
|
-
*/
|
|
277
|
-
function buildSerializedOutput(satoshis, script) {
|
|
278
|
-
const writer = new Utils.Writer();
|
|
279
|
-
writer.writeUInt64LEBn(new BigNumber(satoshis));
|
|
280
|
-
writer.writeVarIntNum(script.length);
|
|
281
|
-
writer.write(script);
|
|
282
|
-
return writer.toArray();
|
|
283
|
-
}
|
|
284
|
-
/**
|
|
285
|
-
* Build OrdLock purchase unlocking script.
|
|
286
|
-
* The purchase path requires no signature - just preimage and output data.
|
|
287
|
-
*/
|
|
288
|
-
async function buildPurchaseUnlockingScript(tx, inputIndex, sourceSatoshis, lockingScript) {
|
|
289
|
-
if (tx.outputs.length < 2) {
|
|
290
|
-
throw new Error("Malformed transaction: requires at least 2 outputs");
|
|
291
|
-
}
|
|
292
|
-
const script = new UnlockingScript()
|
|
293
|
-
.writeBin(buildSerializedOutput(tx.outputs[0].satoshis ?? 0, tx.outputs[0].lockingScript.toBinary()));
|
|
294
|
-
if (tx.outputs.length > 2) {
|
|
295
|
-
const writer = new Utils.Writer();
|
|
296
|
-
for (const output of tx.outputs.slice(2)) {
|
|
297
|
-
writer.write(buildSerializedOutput(output.satoshis ?? 0, output.lockingScript.toBinary()));
|
|
418
|
+
catch (error) {
|
|
419
|
+
return { error: error instanceof Error ? error.message : "unknown-error" };
|
|
298
420
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
else {
|
|
302
|
-
script.writeOpCode(OP.OP_0);
|
|
303
|
-
}
|
|
304
|
-
const input = tx.inputs[inputIndex];
|
|
305
|
-
const sourceTXID = input.sourceTXID ?? input.sourceTransaction?.id("hex");
|
|
306
|
-
if (!sourceTXID) {
|
|
307
|
-
throw new Error("sourceTXID is required");
|
|
308
|
-
}
|
|
309
|
-
const preimage = TransactionSignature.format({
|
|
310
|
-
sourceTXID,
|
|
311
|
-
sourceOutputIndex: input.sourceOutputIndex,
|
|
312
|
-
sourceSatoshis,
|
|
313
|
-
transactionVersion: tx.version,
|
|
314
|
-
otherInputs: [],
|
|
315
|
-
inputIndex,
|
|
316
|
-
outputs: tx.outputs,
|
|
317
|
-
inputSequence: input.sequence ?? 0xffffffff,
|
|
318
|
-
subscript: lockingScript,
|
|
319
|
-
lockTime: tx.lockTime,
|
|
320
|
-
scope: TransactionSignature.SIGHASH_ALL |
|
|
321
|
-
TransactionSignature.SIGHASH_ANYONECANPAY |
|
|
322
|
-
TransactionSignature.SIGHASH_FORKID
|
|
323
|
-
});
|
|
324
|
-
return script.writeBin(preimage).writeOpCode(OP.OP_0);
|
|
325
|
-
}
|
|
421
|
+
},
|
|
422
|
+
};
|
|
326
423
|
/**
|
|
327
424
|
* Purchase an ordinal from the global orderbook.
|
|
328
|
-
*
|
|
329
|
-
* Flow:
|
|
330
|
-
* 1. Fetch listing BEEF to get the locking script
|
|
331
|
-
* 2. Decode OrdLock to get price and payout
|
|
332
|
-
* 3. Build P2PKH output for buyer
|
|
333
|
-
* 4. Build payment output for seller
|
|
334
|
-
* 5. Build custom OrdLock purchase unlock (preimage only, no signature)
|
|
335
425
|
*/
|
|
336
|
-
export
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
// Decode OrdLock from listing script
|
|
360
|
-
const ordLockData = OrdLock.decode(listingOutput.lockingScript);
|
|
361
|
-
if (!ordLockData) {
|
|
362
|
-
return { error: "not-an-ordlock-listing" };
|
|
363
|
-
}
|
|
364
|
-
// Derive our ordinal receive address
|
|
365
|
-
const { publicKey } = await cwi.getPublicKey({
|
|
366
|
-
protocolID: [1, "ordinals"],
|
|
367
|
-
keyID: outpoint,
|
|
368
|
-
counterparty: "self",
|
|
369
|
-
forSelf: true,
|
|
370
|
-
});
|
|
371
|
-
const ourOrdAddress = PublicKey.fromString(publicKey).toAddress();
|
|
372
|
-
// Build outputs
|
|
373
|
-
const outputs = [];
|
|
374
|
-
// Output 0: Ordinal to buyer (P2PKH)
|
|
375
|
-
const p2pkh = new P2PKH();
|
|
376
|
-
outputs.push({
|
|
377
|
-
lockingScript: p2pkh.lock(ourOrdAddress).toHex(),
|
|
378
|
-
satoshis: 1,
|
|
379
|
-
outputDescription: "Purchased ordinal",
|
|
380
|
-
basket: ORDINALS_BASKET,
|
|
381
|
-
});
|
|
382
|
-
// Output 1: Payment to seller (from payout in OrdLock)
|
|
383
|
-
// Parse payout: 8-byte LE satoshis + varint script length + script
|
|
384
|
-
const payoutReader = new Utils.Reader(ordLockData.payout);
|
|
385
|
-
const payoutSatoshis = payoutReader.readUInt64LEBn().toNumber();
|
|
386
|
-
const payoutScriptLen = payoutReader.readVarIntNum();
|
|
387
|
-
const payoutScriptBin = payoutReader.read(payoutScriptLen);
|
|
388
|
-
const payoutLockingScript = LockingScript.fromBinary(payoutScriptBin);
|
|
389
|
-
outputs.push({
|
|
390
|
-
lockingScript: payoutLockingScript.toHex(),
|
|
391
|
-
satoshis: payoutSatoshis,
|
|
392
|
-
outputDescription: "Payment to seller",
|
|
393
|
-
});
|
|
394
|
-
// Output 2+ (optional): Marketplace fee
|
|
395
|
-
if (marketplaceAddress && marketplaceRate && marketplaceRate > 0) {
|
|
396
|
-
const marketFee = Math.ceil(payoutSatoshis * marketplaceRate);
|
|
397
|
-
if (marketFee > 0) {
|
|
398
|
-
outputs.push({
|
|
399
|
-
lockingScript: p2pkh.lock(marketplaceAddress).toHex(),
|
|
400
|
-
satoshis: marketFee,
|
|
401
|
-
outputDescription: "Marketplace fee",
|
|
402
|
-
});
|
|
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" };
|
|
403
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
|
+
};
|
|
404
549
|
}
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
const createResult = await cwi.createAction({
|
|
408
|
-
description: `Purchase ordinal for ${payoutSatoshis} sats`,
|
|
409
|
-
inputBEEF: beef.toBinary(),
|
|
410
|
-
inputs: [{
|
|
411
|
-
outpoint,
|
|
412
|
-
inputDescription: "Listed ordinal",
|
|
413
|
-
unlockingScriptLength: 500, // Estimate for purchase unlock (preimage + outputs)
|
|
414
|
-
}],
|
|
415
|
-
outputs,
|
|
416
|
-
options: { signAndProcess: false },
|
|
417
|
-
});
|
|
418
|
-
if ("error" in createResult && createResult.error) {
|
|
419
|
-
return { error: String(createResult.error) };
|
|
420
|
-
}
|
|
421
|
-
if (!createResult.signableTransaction) {
|
|
422
|
-
return { error: "no-signable-transaction" };
|
|
423
|
-
}
|
|
424
|
-
// Parse the transaction to build purchase unlock
|
|
425
|
-
const tx = Transaction.fromBEEF(createResult.signableTransaction.tx);
|
|
426
|
-
// Build purchase unlocking script
|
|
427
|
-
const unlockingScript = await buildPurchaseUnlockingScript(tx, 0, listingOutput.satoshis ?? 1, listingOutput.lockingScript);
|
|
428
|
-
// Sign and broadcast
|
|
429
|
-
const signResult = await cwi.signAction({
|
|
430
|
-
reference: createResult.signableTransaction.reference,
|
|
431
|
-
spends: {
|
|
432
|
-
0: { unlockingScript: unlockingScript.toHex() },
|
|
433
|
-
},
|
|
434
|
-
});
|
|
435
|
-
if ("error" in signResult) {
|
|
436
|
-
return { error: String(signResult.error) };
|
|
550
|
+
catch (error) {
|
|
551
|
+
return { error: error instanceof Error ? error.message : "unknown-error" };
|
|
437
552
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
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
|
+
];
|