@1sat/actions 0.0.190 → 0.0.192
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/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/inscriptions/index.d.ts +2 -2
- package/dist/inscriptions/index.d.ts.map +1 -1
- package/dist/inscriptions/index.js +3 -6
- package/dist/inscriptions/index.js.map +1 -1
- package/dist/locks/index.d.ts +32 -3
- package/dist/locks/index.d.ts.map +1 -1
- package/dist/locks/index.js +76 -5
- package/dist/locks/index.js.map +1 -1
- package/dist/opns/index.d.ts +43 -76
- package/dist/opns/index.d.ts.map +1 -1
- package/dist/opns/index.js +420 -234
- package/dist/opns/index.js.map +1 -1
- package/dist/ordinals/index.d.ts +58 -44
- package/dist/ordinals/index.d.ts.map +1 -1
- package/dist/ordinals/index.js +217 -188
- package/dist/ordinals/index.js.map +1 -1
- package/dist/tokens/index.d.ts +3 -3
- package/dist/tokens/index.d.ts.map +1 -1
- package/dist/tokens/index.js +38 -24
- package/dist/tokens/index.js.map +1 -1
- package/dist/utils/createTrackedAction.js +1 -1
- package/dist/utils/internalizeBeef.d.ts.map +1 -1
- package/dist/utils/internalizeBeef.js +1 -25
- package/dist/utils/internalizeBeef.js.map +1 -1
- package/dist/utils/loadBasketOutput.d.ts +25 -0
- package/dist/utils/loadBasketOutput.d.ts.map +1 -0
- package/dist/utils/loadBasketOutput.js +50 -0
- package/dist/utils/loadBasketOutput.js.map +1 -0
- package/dist/utils/ordinalSeedTags.d.ts +8 -0
- package/dist/utils/ordinalSeedTags.d.ts.map +1 -0
- package/dist/utils/ordinalSeedTags.js +24 -0
- package/dist/utils/ordinalSeedTags.js.map +1 -0
- package/package.json +9 -9
- package/dist/utils/resolveBeef.d.ts +0 -20
- package/dist/utils/resolveBeef.d.ts.map +0 -1
- package/dist/utils/resolveBeef.js +0 -36
- package/dist/utils/resolveBeef.js.map +0 -1
package/dist/opns/index.js
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* OpNS Module
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Wallet-owned names live in OPNS_BASKET with id: tags.
|
|
5
|
+
* Self-moves: id → one loadBasketOutputBeef → ordinalSeedTags + domain tags.
|
|
6
|
+
* Ingress (internalizeOpns / buyOpns) stamps full tags including id:.
|
|
7
7
|
*/
|
|
8
|
-
import { OpNS } from '@1sat/templates';
|
|
9
|
-
import { OPNS_BASKET, OPNS_PUBLISHED_TAG, OPNS_PUSHDROP_TEMPLATE, OPNS_REGISTER_COUNTERPARTY, P1SAT_PROTOCOL, opnsRegisterKeyId, } from '@1sat/types';
|
|
10
|
-
import { PushDrop, Transaction, Utils, } from '@bsv/sdk';
|
|
11
|
-
import {
|
|
8
|
+
import { OpNS, OrdLock } from '@1sat/templates';
|
|
9
|
+
import { OPNS_BASKET, OPNS_PUBLISHED_TAG, OPNS_PUSHDROP_TEMPLATE, OPNS_REGISTER_COUNTERPARTY, P1SAT_PROTOCOL, buildInputAssetLabel, opnsRegisterKeyId, readAssetIdTag, } from '@1sat/types';
|
|
10
|
+
import { P2PKH, PublicKey, PushDrop, Transaction, Utils, } from '@bsv/sdk';
|
|
11
|
+
import { buildOrdLockScript, buyOrdinal, defaultPayAddress, deriveCancelAddressInternal, } from '../ordinals';
|
|
12
12
|
import { executeTrackedAction, randomActionId, } from '../utils/createTrackedAction';
|
|
13
|
-
import {
|
|
13
|
+
import { loadBasketOutputBeef } from '../utils/loadBasketOutput';
|
|
14
|
+
import { ordinalSeedTags } from '../utils/ordinalSeedTags';
|
|
14
15
|
import { signOrdinalInput, unlockingScriptLengthForInstructions, } from '../utils/signOrdinalInput';
|
|
16
|
+
const OPNS_CONTENT_TYPE = 'application/op-ns';
|
|
15
17
|
export { opnsRegisterKeyId } from '@1sat/types';
|
|
16
|
-
|
|
18
|
+
// ============================================================================
|
|
19
|
+
// Helpers
|
|
20
|
+
// ============================================================================
|
|
17
21
|
function findMintNameDelivery(tx) {
|
|
18
22
|
for (let i = 0; i + 2 < tx.outputs.length; i++) {
|
|
19
23
|
const parent = OpNS.decode(tx.outputs[i].lockingScript);
|
|
@@ -27,79 +31,59 @@ function findMintNameDelivery(tx) {
|
|
|
27
31
|
}
|
|
28
32
|
return null;
|
|
29
33
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
function nameFromOutput(out) {
|
|
35
|
+
const fromTag = out.tags?.find((t) => t.startsWith('name:'))?.slice(5);
|
|
36
|
+
if (fromTag)
|
|
37
|
+
return fromTag.slice(0, 64);
|
|
38
|
+
if (!out.customInstructions)
|
|
39
|
+
return undefined;
|
|
40
|
+
try {
|
|
41
|
+
const n = JSON.parse(out.customInstructions).name;
|
|
42
|
+
return typeof n === 'string' && n ? n.slice(0, 64) : undefined;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return undefined;
|
|
41
46
|
}
|
|
42
|
-
return ordinal.tags?.find((t) => t.startsWith('name:'))?.slice(5);
|
|
43
47
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
48
|
+
/** Load OPNS row + BEEF from wallet storage by id. */
|
|
49
|
+
async function loadOpnsSpend(ctx, input) {
|
|
50
|
+
return loadBasketOutputBeef(ctx.wallet, OPNS_BASKET, input.id);
|
|
51
|
+
}
|
|
52
|
+
/** Ordinal seed + opns domain tag; optional extras (ordlock, published, …). */
|
|
53
|
+
function opnsFileTags(output, extra = []) {
|
|
54
|
+
const tags = ordinalSeedTags(output);
|
|
55
|
+
if (!tags.includes('opns'))
|
|
56
|
+
tags.unshift('opns');
|
|
57
|
+
for (const e of extra) {
|
|
58
|
+
if (e && !tags.includes(e))
|
|
59
|
+
tags.push(e);
|
|
47
60
|
}
|
|
48
|
-
return
|
|
49
|
-
sign: async (tx) => {
|
|
50
|
-
const unlocking = await signOrdinalInput(ctx, tx, 0, ordinal.customInstructions);
|
|
51
|
-
if (typeof unlocking !== 'string')
|
|
52
|
-
throw new Error(unlocking.error);
|
|
53
|
-
return { 0: { unlockingScript: unlocking } };
|
|
54
|
-
},
|
|
55
|
-
};
|
|
61
|
+
return tags;
|
|
56
62
|
}
|
|
57
63
|
// ============================================================================
|
|
58
|
-
//
|
|
64
|
+
// listOpns
|
|
59
65
|
// ============================================================================
|
|
60
|
-
|
|
61
|
-
* List OpNS names in the wallet. Mirrors listOutputs (fixed OPNS basket).
|
|
62
|
-
* Metadata-only by default; pass include: 'entire transactions' for batch BEEF.
|
|
63
|
-
*/
|
|
64
|
-
export const getOpnsNames = {
|
|
66
|
+
export const listOpns = {
|
|
65
67
|
meta: {
|
|
66
|
-
name: '
|
|
68
|
+
name: 'listOpns',
|
|
67
69
|
description: 'List OpNS names from the wallet (metadata by default; optional BEEF)',
|
|
68
70
|
category: 'opns',
|
|
69
71
|
inputSchema: {
|
|
70
72
|
type: 'object',
|
|
71
73
|
properties: {
|
|
72
|
-
tags: {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
},
|
|
77
|
-
tagQueryMode: {
|
|
78
|
-
type: 'string',
|
|
79
|
-
enum: ['all', 'any'],
|
|
80
|
-
description: 'How tags combine (default any when filtering)',
|
|
81
|
-
},
|
|
82
|
-
names: {
|
|
83
|
-
type: 'array',
|
|
84
|
-
description: 'Appended as name:${n} tags',
|
|
85
|
-
items: { type: 'string' },
|
|
86
|
-
},
|
|
74
|
+
tags: { type: 'array', items: { type: 'string' } },
|
|
75
|
+
tagQueryMode: { type: 'string', enum: ['all', 'any'] },
|
|
76
|
+
names: { type: 'array', items: { type: 'string' } },
|
|
77
|
+
ids: { type: 'array', items: { type: 'string' } },
|
|
87
78
|
include: {
|
|
88
79
|
type: 'string',
|
|
89
80
|
enum: ['locking scripts', 'entire transactions'],
|
|
90
|
-
description: 'Omit for metadata-only (no BEEF)',
|
|
91
81
|
},
|
|
92
82
|
includeCustomInstructions: { type: 'boolean' },
|
|
93
83
|
includeTags: { type: 'boolean' },
|
|
94
84
|
includeLabels: { type: 'boolean' },
|
|
95
|
-
limit: {
|
|
96
|
-
|
|
97
|
-
description: 'Max names to return (default: 100)',
|
|
98
|
-
},
|
|
99
|
-
offset: {
|
|
100
|
-
type: 'integer',
|
|
101
|
-
description: 'Offset for pagination (default: 0)',
|
|
102
|
-
},
|
|
85
|
+
limit: { type: 'integer' },
|
|
86
|
+
offset: { type: 'integer' },
|
|
103
87
|
},
|
|
104
88
|
},
|
|
105
89
|
},
|
|
@@ -109,6 +93,10 @@ export const getOpnsNames = {
|
|
|
109
93
|
if (n)
|
|
110
94
|
tags.push(`name:${n}`);
|
|
111
95
|
}
|
|
96
|
+
for (const id of input.ids ?? []) {
|
|
97
|
+
if (id)
|
|
98
|
+
tags.push(id.startsWith('id:') ? id : `id:${id}`);
|
|
99
|
+
}
|
|
112
100
|
const filtering = tags.length > 0;
|
|
113
101
|
const result = await ctx.wallet.listOutputs({
|
|
114
102
|
basket: OPNS_BASKET,
|
|
@@ -132,51 +120,38 @@ export const getOpnsNames = {
|
|
|
132
120
|
};
|
|
133
121
|
},
|
|
134
122
|
};
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
*/
|
|
123
|
+
// ============================================================================
|
|
124
|
+
// internalizeOpns (ingress)
|
|
125
|
+
// ============================================================================
|
|
139
126
|
export const internalizeOpns = {
|
|
140
127
|
meta: {
|
|
141
128
|
name: 'internalizeOpns',
|
|
142
|
-
description: 'Internalize a foreign-created OpNS mint into the
|
|
129
|
+
description: 'Internalize a foreign-created OpNS mint (AtomicBEEF) into the OPNS basket',
|
|
143
130
|
category: 'opns',
|
|
144
131
|
inputSchema: {
|
|
145
132
|
type: 'object',
|
|
146
133
|
properties: {
|
|
147
|
-
tx: {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
},
|
|
152
|
-
protocolID: {
|
|
153
|
-
type: 'array',
|
|
154
|
-
description: 'Wallet protocol for spend derivation',
|
|
155
|
-
},
|
|
156
|
-
keyID: {
|
|
157
|
-
type: 'string',
|
|
158
|
-
description: 'Key ID for spend derivation',
|
|
159
|
-
},
|
|
160
|
-
counterparty: {
|
|
161
|
-
type: 'string',
|
|
162
|
-
description: 'Counterparty for spend derivation (default self)',
|
|
163
|
-
},
|
|
134
|
+
tx: { type: 'array', items: { type: 'integer' } },
|
|
135
|
+
protocolID: { type: 'array' },
|
|
136
|
+
keyID: { type: 'string' },
|
|
137
|
+
counterparty: { type: 'string' },
|
|
164
138
|
},
|
|
165
139
|
required: ['tx', 'protocolID', 'keyID'],
|
|
166
140
|
},
|
|
167
141
|
},
|
|
168
|
-
async execute(
|
|
142
|
+
async execute(ctx, input) {
|
|
169
143
|
try {
|
|
170
144
|
const parsed = Transaction.fromAtomicBEEF(input.tx);
|
|
171
145
|
const txid = parsed.id('hex');
|
|
172
146
|
const delivery = findMintNameDelivery(parsed);
|
|
173
|
-
if (!delivery)
|
|
147
|
+
if (!delivery)
|
|
174
148
|
return { error: 'not-an-opns-mint' };
|
|
175
|
-
}
|
|
176
149
|
const { vout, name } = delivery;
|
|
150
|
+
const outpoint = `${txid}.${vout}`;
|
|
177
151
|
const actionId = randomActionId();
|
|
152
|
+
const idTag = `id:${actionId}_${vout}`;
|
|
178
153
|
const counterparty = input.counterparty ?? 'self';
|
|
179
|
-
await
|
|
154
|
+
await ctx.wallet.internalizeAction({
|
|
180
155
|
tx: input.tx,
|
|
181
156
|
outputs: [
|
|
182
157
|
{
|
|
@@ -184,7 +159,13 @@ export const internalizeOpns = {
|
|
|
184
159
|
protocol: 'basket insertion',
|
|
185
160
|
insertionRemittance: {
|
|
186
161
|
basket: OPNS_BASKET,
|
|
187
|
-
tags: [
|
|
162
|
+
tags: [
|
|
163
|
+
'opns',
|
|
164
|
+
`type:${OPNS_CONTENT_TYPE}`,
|
|
165
|
+
`origin:${outpoint}`,
|
|
166
|
+
`name:${name}`,
|
|
167
|
+
idTag,
|
|
168
|
+
],
|
|
188
169
|
customInstructions: JSON.stringify({
|
|
189
170
|
protocolID: input.protocolID,
|
|
190
171
|
keyID: input.keyID,
|
|
@@ -198,8 +179,9 @@ export const internalizeOpns = {
|
|
|
198
179
|
});
|
|
199
180
|
return {
|
|
200
181
|
txid,
|
|
201
|
-
outpoint
|
|
182
|
+
outpoint,
|
|
202
183
|
name,
|
|
184
|
+
id: `${actionId}_${vout}`,
|
|
203
185
|
};
|
|
204
186
|
}
|
|
205
187
|
catch (err) {
|
|
@@ -209,58 +191,50 @@ export const internalizeOpns = {
|
|
|
209
191
|
}
|
|
210
192
|
},
|
|
211
193
|
};
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
* fields[0] = identity pubkey bytes; field-sig included (same derivation).
|
|
217
|
-
*/
|
|
218
|
-
export const opnsRegister = {
|
|
194
|
+
// ============================================================================
|
|
195
|
+
// register / deregister
|
|
196
|
+
// ============================================================================
|
|
197
|
+
export const registerOpns = {
|
|
219
198
|
meta: {
|
|
220
|
-
name: '
|
|
199
|
+
name: 'registerOpns',
|
|
221
200
|
description: 'Bind BRC-100 identity key to an OpNS name via signed PushDrop',
|
|
222
201
|
category: 'opns',
|
|
223
202
|
inputSchema: {
|
|
224
203
|
type: 'object',
|
|
225
204
|
properties: {
|
|
226
|
-
|
|
227
|
-
type: 'object',
|
|
228
|
-
description: 'WalletOutput of the OpNS ordinal from listOutputs',
|
|
229
|
-
},
|
|
230
|
-
inputBEEF: {
|
|
231
|
-
type: 'array',
|
|
232
|
-
description: "BEEF from listOutputs with include: 'entire transactions'",
|
|
233
|
-
},
|
|
205
|
+
id: { type: 'string', description: 'OPNS basket tracking id' },
|
|
234
206
|
},
|
|
235
|
-
required: ['
|
|
207
|
+
required: ['id'],
|
|
236
208
|
},
|
|
237
209
|
},
|
|
238
210
|
async execute(ctx, input) {
|
|
239
211
|
try {
|
|
240
|
-
const
|
|
241
|
-
if (
|
|
212
|
+
const loaded = await loadOpnsSpend(ctx, input);
|
|
213
|
+
if ('error' in loaded)
|
|
214
|
+
return loaded;
|
|
215
|
+
const { output, beef } = loaded;
|
|
216
|
+
if (!output.customInstructions) {
|
|
242
217
|
return { error: 'missing-custom-instructions' };
|
|
243
218
|
}
|
|
244
|
-
const inputBEEF = input.inputBEEF ??
|
|
245
|
-
(await resolveBeef(ctx.wallet, OPNS_BASKET, ordinal));
|
|
246
219
|
const { publicKey: identityPubKey } = await ctx.wallet.getPublicKey({
|
|
247
220
|
identityKey: true,
|
|
248
221
|
});
|
|
249
|
-
const keyID = opnsRegisterKeyId(
|
|
222
|
+
const keyID = opnsRegisterKeyId(output.outpoint);
|
|
250
223
|
const lockingScript = await new PushDrop(ctx.wallet).lock([Utils.toArray(identityPubKey, 'hex')], P1SAT_PROTOCOL, keyID, OPNS_REGISTER_COUNTERPARTY, true, true);
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
const name = sourceNameFromOrdinal(ordinal);
|
|
256
|
-
const result = await executeTrackedAction(ctx.wallet, {
|
|
224
|
+
const name = nameFromOutput(output);
|
|
225
|
+
const tags = opnsFileTags(output, [OPNS_PUBLISHED_TAG]);
|
|
226
|
+
const inputId = readAssetIdTag(output.tags);
|
|
227
|
+
return await executeTrackedAction(ctx.wallet, {
|
|
257
228
|
description: 'Register OpNS identity bind',
|
|
258
|
-
inputBEEF,
|
|
229
|
+
inputBEEF: beef,
|
|
230
|
+
...(inputId && {
|
|
231
|
+
labels: [buildInputAssetLabel(OPNS_BASKET, inputId)],
|
|
232
|
+
}),
|
|
259
233
|
inputs: [
|
|
260
234
|
{
|
|
261
|
-
outpoint:
|
|
235
|
+
outpoint: output.outpoint,
|
|
262
236
|
inputDescription: 'OpNS name to register',
|
|
263
|
-
unlockingScriptLength: unlockingScriptLengthForInstructions(
|
|
237
|
+
unlockingScriptLength: unlockingScriptLengthForInstructions(output.customInstructions),
|
|
264
238
|
},
|
|
265
239
|
],
|
|
266
240
|
outputs: [
|
|
@@ -280,186 +254,398 @@ export const opnsRegister = {
|
|
|
280
254
|
},
|
|
281
255
|
],
|
|
282
256
|
options: { randomizeOutputs: false },
|
|
283
|
-
}, input.fundingProvider,
|
|
284
|
-
const unlocking = await signOrdinalInput(ctx, tx, 0,
|
|
257
|
+
}, input.fundingProvider, beef, async (tx) => {
|
|
258
|
+
const unlocking = await signOrdinalInput(ctx, tx, 0, output.customInstructions);
|
|
285
259
|
if (typeof unlocking !== 'string')
|
|
286
260
|
throw new Error(unlocking.error);
|
|
287
261
|
return { 0: { unlockingScript: unlocking } };
|
|
288
262
|
});
|
|
289
|
-
return result;
|
|
290
263
|
}
|
|
291
264
|
catch (error) {
|
|
292
|
-
console.error('[
|
|
293
|
-
if (ctx.debug && ctx.log) {
|
|
294
|
-
ctx.log({
|
|
295
|
-
timestamp: new Date().toISOString(),
|
|
296
|
-
action: 'opnsRegister',
|
|
297
|
-
input: { outpoint: input.ordinal.outpoint },
|
|
298
|
-
error: error instanceof Error ? error.message : 'unknown-error',
|
|
299
|
-
});
|
|
300
|
-
}
|
|
265
|
+
console.error('[registerOpns]', error);
|
|
301
266
|
return {
|
|
302
267
|
error: error instanceof Error ? error.message : 'unknown-error',
|
|
303
268
|
};
|
|
304
269
|
}
|
|
305
270
|
},
|
|
306
271
|
};
|
|
307
|
-
|
|
308
|
-
* Remove an identity bind by self-transferring to plain P2PKH.
|
|
309
|
-
* Spending the PushDrop clears the on-chain bind.
|
|
310
|
-
*/
|
|
311
|
-
export const opnsDeregister = {
|
|
272
|
+
export const deregisterOpns = {
|
|
312
273
|
meta: {
|
|
313
|
-
name: '
|
|
274
|
+
name: 'deregisterOpns',
|
|
314
275
|
description: 'Remove identity bind from an OpNS name (self-transfer to P2PKH)',
|
|
315
276
|
category: 'opns',
|
|
316
277
|
inputSchema: {
|
|
317
278
|
type: 'object',
|
|
318
279
|
properties: {
|
|
319
|
-
|
|
320
|
-
type: 'object',
|
|
321
|
-
description: 'WalletOutput of the OpNS ordinal from listOutputs',
|
|
322
|
-
},
|
|
323
|
-
inputBEEF: {
|
|
324
|
-
type: 'array',
|
|
325
|
-
description: "BEEF from listOutputs with include: 'entire transactions'",
|
|
326
|
-
},
|
|
280
|
+
id: { type: 'string' },
|
|
327
281
|
},
|
|
328
|
-
required: ['
|
|
282
|
+
required: ['id'],
|
|
329
283
|
},
|
|
330
284
|
},
|
|
331
285
|
async execute(ctx, input) {
|
|
332
286
|
try {
|
|
333
|
-
const
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
287
|
+
const loaded = await loadOpnsSpend(ctx, input);
|
|
288
|
+
if ('error' in loaded)
|
|
289
|
+
return loaded;
|
|
290
|
+
const { output, beef } = loaded;
|
|
291
|
+
if (!output.customInstructions) {
|
|
292
|
+
return { error: 'missing-custom-instructions' };
|
|
293
|
+
}
|
|
294
|
+
const outpoint = output.outpoint;
|
|
295
|
+
const { publicKey } = await ctx.wallet.getPublicKey({
|
|
296
|
+
protocolID: P1SAT_PROTOCOL,
|
|
297
|
+
keyID: outpoint,
|
|
298
|
+
counterparty: 'self',
|
|
299
|
+
forSelf: true,
|
|
300
|
+
});
|
|
301
|
+
const address = PublicKey.fromString(publicKey).toAddress();
|
|
302
|
+
const tags = opnsFileTags(output);
|
|
303
|
+
const name = nameFromOutput(output);
|
|
304
|
+
const inputId = readAssetIdTag(output.tags);
|
|
305
|
+
return await executeTrackedAction(ctx.wallet, {
|
|
306
|
+
description: 'Deregister OpNS identity bind',
|
|
307
|
+
inputBEEF: beef,
|
|
308
|
+
...(inputId && {
|
|
309
|
+
labels: [buildInputAssetLabel(OPNS_BASKET, inputId)],
|
|
310
|
+
}),
|
|
311
|
+
inputs: [
|
|
337
312
|
{
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
313
|
+
outpoint,
|
|
314
|
+
inputDescription: 'OpNS name to deregister',
|
|
315
|
+
unlockingScriptLength: unlockingScriptLengthForInstructions(output.customInstructions),
|
|
341
316
|
},
|
|
342
317
|
],
|
|
343
|
-
|
|
318
|
+
outputs: [
|
|
319
|
+
{
|
|
320
|
+
lockingScript: new P2PKH().lock(address).toHex(),
|
|
321
|
+
satoshis: 1,
|
|
322
|
+
outputDescription: 'OpNS name (unbound)',
|
|
323
|
+
basket: OPNS_BASKET,
|
|
324
|
+
tags,
|
|
325
|
+
customInstructions: JSON.stringify({
|
|
326
|
+
protocolID: P1SAT_PROTOCOL,
|
|
327
|
+
keyID: outpoint,
|
|
328
|
+
...(name && { name }),
|
|
329
|
+
}),
|
|
330
|
+
},
|
|
331
|
+
],
|
|
332
|
+
options: { randomizeOutputs: false },
|
|
333
|
+
}, input.fundingProvider, beef, async (tx) => {
|
|
334
|
+
const unlocking = await signOrdinalInput(ctx, tx, 0, output.customInstructions);
|
|
335
|
+
if (typeof unlocking !== 'string')
|
|
336
|
+
throw new Error(unlocking.error);
|
|
337
|
+
return { 0: { unlockingScript: unlocking } };
|
|
344
338
|
});
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
339
|
+
}
|
|
340
|
+
catch (error) {
|
|
341
|
+
console.error('[deregisterOpns]', error);
|
|
342
|
+
return {
|
|
343
|
+
error: error instanceof Error ? error.message : 'unknown-error',
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
},
|
|
347
|
+
};
|
|
348
|
+
// ============================================================================
|
|
349
|
+
// sell / send / cancel
|
|
350
|
+
// ============================================================================
|
|
351
|
+
export const sellOpns = {
|
|
352
|
+
meta: {
|
|
353
|
+
name: 'sellOpns',
|
|
354
|
+
description: 'List an OpNS name for sale',
|
|
355
|
+
category: 'opns',
|
|
356
|
+
inputSchema: {
|
|
357
|
+
type: 'object',
|
|
358
|
+
properties: {
|
|
359
|
+
id: { type: 'string' },
|
|
360
|
+
price: { type: 'integer' },
|
|
361
|
+
payAddress: {
|
|
362
|
+
type: 'string',
|
|
363
|
+
description: 'Payment address (default: P1SAT keyID 1sat 0)',
|
|
364
|
+
},
|
|
365
|
+
},
|
|
366
|
+
required: ['id', 'price'],
|
|
367
|
+
},
|
|
368
|
+
},
|
|
369
|
+
async execute(ctx, input) {
|
|
370
|
+
try {
|
|
371
|
+
if (input.price <= 0)
|
|
372
|
+
return { error: 'invalid-price' };
|
|
373
|
+
const loaded = await loadOpnsSpend(ctx, input);
|
|
374
|
+
if ('error' in loaded)
|
|
375
|
+
return loaded;
|
|
376
|
+
const { output, beef } = loaded;
|
|
377
|
+
if (!output.customInstructions) {
|
|
378
|
+
return { error: 'missing-custom-instructions' };
|
|
351
379
|
}
|
|
352
|
-
const
|
|
353
|
-
|
|
354
|
-
|
|
380
|
+
const outpoint = output.outpoint;
|
|
381
|
+
const payAddress = input.payAddress ?? (await defaultPayAddress(ctx));
|
|
382
|
+
const cancelAddress = await deriveCancelAddressInternal(ctx, outpoint);
|
|
383
|
+
const lockingScript = buildOrdLockScript(cancelAddress, payAddress, input.price).toHex();
|
|
384
|
+
const tags = opnsFileTags(output, ['ordlock', `price:${input.price}`]);
|
|
385
|
+
const name = nameFromOutput(output);
|
|
386
|
+
const inputId = readAssetIdTag(output.tags);
|
|
355
387
|
return await executeTrackedAction(ctx.wallet, {
|
|
356
|
-
|
|
357
|
-
|
|
388
|
+
description: `List OpNS for ${input.price} sats`,
|
|
389
|
+
inputBEEF: beef,
|
|
390
|
+
...(inputId && {
|
|
391
|
+
labels: [buildInputAssetLabel(OPNS_BASKET, inputId)],
|
|
392
|
+
}),
|
|
393
|
+
inputs: [
|
|
394
|
+
{
|
|
395
|
+
outpoint,
|
|
396
|
+
inputDescription: 'OpNS name to list',
|
|
397
|
+
unlockingScriptLength: unlockingScriptLengthForInstructions(output.customInstructions),
|
|
398
|
+
},
|
|
399
|
+
],
|
|
400
|
+
outputs: [
|
|
401
|
+
{
|
|
402
|
+
lockingScript,
|
|
403
|
+
satoshis: 1,
|
|
404
|
+
outputDescription: `List OpNS for ${input.price} sats`,
|
|
405
|
+
basket: OPNS_BASKET,
|
|
406
|
+
tags,
|
|
407
|
+
customInstructions: JSON.stringify({
|
|
408
|
+
protocolID: P1SAT_PROTOCOL,
|
|
409
|
+
keyID: outpoint,
|
|
410
|
+
...(name && { name }),
|
|
411
|
+
}),
|
|
412
|
+
},
|
|
413
|
+
],
|
|
358
414
|
options: { randomizeOutputs: false },
|
|
359
|
-
}, input.fundingProvider,
|
|
415
|
+
}, input.fundingProvider, beef, async (tx) => {
|
|
416
|
+
const unlocking = await signOrdinalInput(ctx, tx, 0, output.customInstructions);
|
|
417
|
+
if (typeof unlocking !== 'string')
|
|
418
|
+
throw new Error(unlocking.error);
|
|
419
|
+
return { 0: { unlockingScript: unlocking } };
|
|
420
|
+
});
|
|
360
421
|
}
|
|
361
422
|
catch (error) {
|
|
362
|
-
console.error('[
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
423
|
+
console.error('[sellOpns]', error);
|
|
424
|
+
return {
|
|
425
|
+
error: error instanceof Error ? error.message : 'unknown-error',
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
},
|
|
429
|
+
};
|
|
430
|
+
export const sendOpns = {
|
|
431
|
+
meta: {
|
|
432
|
+
name: 'sendOpns',
|
|
433
|
+
description: 'Transfer an OpNS name to a new owner',
|
|
434
|
+
category: 'opns',
|
|
435
|
+
inputSchema: {
|
|
436
|
+
type: 'object',
|
|
437
|
+
properties: {
|
|
438
|
+
id: { type: 'string' },
|
|
439
|
+
counterparty: { type: 'string' },
|
|
440
|
+
address: { type: 'string' },
|
|
441
|
+
},
|
|
442
|
+
required: ['id'],
|
|
443
|
+
},
|
|
444
|
+
},
|
|
445
|
+
async execute(ctx, input) {
|
|
446
|
+
try {
|
|
447
|
+
if (!input.counterparty && !input.address) {
|
|
448
|
+
return { error: 'must-provide-counterparty-or-address' };
|
|
449
|
+
}
|
|
450
|
+
const loaded = await loadOpnsSpend(ctx, input);
|
|
451
|
+
if ('error' in loaded)
|
|
452
|
+
return loaded;
|
|
453
|
+
const { output, beef } = loaded;
|
|
454
|
+
if (!output.customInstructions) {
|
|
455
|
+
return { error: 'missing-custom-instructions' };
|
|
456
|
+
}
|
|
457
|
+
const outpoint = output.outpoint;
|
|
458
|
+
const isSelf = input.counterparty === 'self';
|
|
459
|
+
let recipientAddress;
|
|
460
|
+
if (input.counterparty) {
|
|
461
|
+
const { publicKey } = await ctx.wallet.getPublicKey({
|
|
462
|
+
protocolID: P1SAT_PROTOCOL,
|
|
463
|
+
keyID: outpoint,
|
|
464
|
+
counterparty: input.counterparty,
|
|
465
|
+
forSelf: isSelf,
|
|
369
466
|
});
|
|
467
|
+
recipientAddress = PublicKey.fromString(publicKey).toAddress();
|
|
468
|
+
}
|
|
469
|
+
else {
|
|
470
|
+
recipientAddress = input.address;
|
|
370
471
|
}
|
|
472
|
+
const inputId = readAssetIdTag(output.tags);
|
|
473
|
+
const name = nameFromOutput(output);
|
|
474
|
+
const tags = opnsFileTags(output);
|
|
475
|
+
return await executeTrackedAction(ctx.wallet, {
|
|
476
|
+
description: 'Transfer OpNS name',
|
|
477
|
+
inputBEEF: beef,
|
|
478
|
+
...(inputId && {
|
|
479
|
+
labels: [buildInputAssetLabel(OPNS_BASKET, inputId)],
|
|
480
|
+
}),
|
|
481
|
+
inputs: [
|
|
482
|
+
{
|
|
483
|
+
outpoint,
|
|
484
|
+
inputDescription: 'OpNS name to transfer',
|
|
485
|
+
unlockingScriptLength: unlockingScriptLengthForInstructions(output.customInstructions),
|
|
486
|
+
},
|
|
487
|
+
],
|
|
488
|
+
outputs: [
|
|
489
|
+
isSelf
|
|
490
|
+
? {
|
|
491
|
+
lockingScript: new P2PKH().lock(recipientAddress).toHex(),
|
|
492
|
+
satoshis: 1,
|
|
493
|
+
outputDescription: 'OpNS self-transfer',
|
|
494
|
+
basket: OPNS_BASKET,
|
|
495
|
+
tags,
|
|
496
|
+
customInstructions: JSON.stringify({
|
|
497
|
+
protocolID: P1SAT_PROTOCOL,
|
|
498
|
+
keyID: outpoint,
|
|
499
|
+
...(name && { name }),
|
|
500
|
+
}),
|
|
501
|
+
}
|
|
502
|
+
: {
|
|
503
|
+
lockingScript: new P2PKH().lock(recipientAddress).toHex(),
|
|
504
|
+
satoshis: 1,
|
|
505
|
+
outputDescription: 'OpNS transfer',
|
|
506
|
+
tags: [],
|
|
507
|
+
},
|
|
508
|
+
],
|
|
509
|
+
options: { randomizeOutputs: false },
|
|
510
|
+
}, input.fundingProvider, beef, async (tx) => {
|
|
511
|
+
const unlocking = await signOrdinalInput(ctx, tx, 0, output.customInstructions);
|
|
512
|
+
if (typeof unlocking !== 'string')
|
|
513
|
+
throw new Error(unlocking.error);
|
|
514
|
+
return { 0: { unlockingScript: unlocking } };
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
catch (error) {
|
|
518
|
+
console.error('[sendOpns]', error);
|
|
371
519
|
return {
|
|
372
520
|
error: error instanceof Error ? error.message : 'unknown-error',
|
|
373
521
|
};
|
|
374
522
|
}
|
|
375
523
|
},
|
|
376
524
|
};
|
|
377
|
-
|
|
378
|
-
* List an OpNS name for sale.
|
|
379
|
-
* Spends the current lock (PushDrop or P2PKH); OrdLock output has no identity bind.
|
|
380
|
-
*/
|
|
381
|
-
export const opnsList = {
|
|
525
|
+
export const cancelOpnsListing = {
|
|
382
526
|
meta: {
|
|
383
|
-
name: '
|
|
384
|
-
description: '
|
|
527
|
+
name: 'cancelOpnsListing',
|
|
528
|
+
description: 'Cancel an OpNS name listing back into the OPNS basket',
|
|
385
529
|
category: 'opns',
|
|
386
530
|
inputSchema: {
|
|
387
531
|
type: 'object',
|
|
388
532
|
properties: {
|
|
389
|
-
|
|
390
|
-
type: 'object',
|
|
391
|
-
description: 'WalletOutput of the OpNS ordinal from listOutputs',
|
|
392
|
-
},
|
|
393
|
-
inputBEEF: {
|
|
394
|
-
type: 'array',
|
|
395
|
-
description: "BEEF from listOutputs with include: 'entire transactions'",
|
|
396
|
-
},
|
|
397
|
-
price: { type: 'integer', description: 'Price in satoshis' },
|
|
398
|
-
payAddress: {
|
|
399
|
-
type: 'string',
|
|
400
|
-
description: 'Address to receive payment on purchase',
|
|
401
|
-
},
|
|
533
|
+
id: { type: 'string' },
|
|
402
534
|
},
|
|
403
|
-
required: ['
|
|
535
|
+
required: ['id'],
|
|
404
536
|
},
|
|
405
537
|
},
|
|
406
538
|
async execute(ctx, input) {
|
|
407
|
-
|
|
539
|
+
try {
|
|
540
|
+
const loaded = await loadOpnsSpend(ctx, input);
|
|
541
|
+
if ('error' in loaded)
|
|
542
|
+
return loaded;
|
|
543
|
+
const { output: listing, beef: inputBEEF } = loaded;
|
|
544
|
+
const outpoint = listing.outpoint;
|
|
545
|
+
if (!listing.customInstructions) {
|
|
546
|
+
return { error: 'missing-custom-instructions' };
|
|
547
|
+
}
|
|
548
|
+
const { protocolID: signProtocolID, keyID: signKeyID, counterparty: signCounterparty, } = JSON.parse(listing.customInstructions);
|
|
549
|
+
const newKeyID = outpoint;
|
|
550
|
+
const cancelAddress = await deriveCancelAddressInternal(ctx, newKeyID);
|
|
551
|
+
const tags = opnsFileTags(listing);
|
|
552
|
+
const name = nameFromOutput(listing);
|
|
553
|
+
const inputId = readAssetIdTag(listing.tags);
|
|
554
|
+
const cancelUnlock = OrdLock.cancelWithWallet(ctx.wallet, signProtocolID, signKeyID, signCounterparty);
|
|
555
|
+
return await executeTrackedAction(ctx.wallet, {
|
|
556
|
+
description: 'Cancel OpNS listing',
|
|
557
|
+
inputBEEF,
|
|
558
|
+
...(inputId && {
|
|
559
|
+
labels: [buildInputAssetLabel(OPNS_BASKET, inputId)],
|
|
560
|
+
}),
|
|
561
|
+
inputs: [
|
|
562
|
+
{
|
|
563
|
+
outpoint,
|
|
564
|
+
inputDescription: 'Listed OpNS name',
|
|
565
|
+
unlockingScriptLength: 108,
|
|
566
|
+
},
|
|
567
|
+
],
|
|
568
|
+
outputs: [
|
|
569
|
+
{
|
|
570
|
+
lockingScript: new P2PKH().lock(cancelAddress).toHex(),
|
|
571
|
+
satoshis: 1,
|
|
572
|
+
outputDescription: 'Cancelled OpNS listing',
|
|
573
|
+
basket: OPNS_BASKET,
|
|
574
|
+
tags,
|
|
575
|
+
customInstructions: JSON.stringify({
|
|
576
|
+
protocolID: P1SAT_PROTOCOL,
|
|
577
|
+
keyID: newKeyID,
|
|
578
|
+
...(name && { name }),
|
|
579
|
+
}),
|
|
580
|
+
},
|
|
581
|
+
],
|
|
582
|
+
options: { randomizeOutputs: false },
|
|
583
|
+
}, input.fundingProvider, inputBEEF, async (tx) => {
|
|
584
|
+
const unlockingScript = await cancelUnlock.sign(tx, 0);
|
|
585
|
+
return { 0: { unlockingScript: unlockingScript.toHex() } };
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
catch (error) {
|
|
589
|
+
console.error('[cancelOpnsListing]', error);
|
|
590
|
+
return {
|
|
591
|
+
error: error instanceof Error ? error.message : 'unknown-error',
|
|
592
|
+
};
|
|
593
|
+
}
|
|
408
594
|
},
|
|
409
595
|
};
|
|
410
596
|
/**
|
|
411
|
-
*
|
|
412
|
-
* Spends the current lock; recipient gets plain P2PKH (bind does not carry forward).
|
|
597
|
+
* Buy an OpNS listing and file into the OPNS basket with full tags.
|
|
413
598
|
*/
|
|
414
|
-
export const
|
|
599
|
+
export const buyOpns = {
|
|
415
600
|
meta: {
|
|
416
|
-
name: '
|
|
417
|
-
description: '
|
|
601
|
+
name: 'buyOpns',
|
|
602
|
+
description: 'Purchase an OpNS name listing into the OPNS basket',
|
|
418
603
|
category: 'opns',
|
|
604
|
+
requiresServices: true,
|
|
419
605
|
inputSchema: {
|
|
420
606
|
type: 'object',
|
|
421
607
|
properties: {
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
},
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
description: 'Recipient identity public key (hex)',
|
|
429
|
-
},
|
|
430
|
-
address: {
|
|
431
|
-
type: 'string',
|
|
432
|
-
description: 'Recipient P2PKH address',
|
|
433
|
-
},
|
|
434
|
-
inputBEEF: {
|
|
435
|
-
type: 'array',
|
|
436
|
-
description: "BEEF from listOutputs with include: 'entire transactions'",
|
|
437
|
-
},
|
|
608
|
+
outpoint: { type: 'string' },
|
|
609
|
+
inputBEEF: { type: 'array', items: { type: 'integer' } },
|
|
610
|
+
name: { type: 'string' },
|
|
611
|
+
origin: { type: 'string' },
|
|
612
|
+
marketplaceAddress: { type: 'string' },
|
|
613
|
+
marketplaceRate: { type: 'number' },
|
|
438
614
|
},
|
|
439
|
-
required: ['
|
|
615
|
+
required: ['outpoint'],
|
|
440
616
|
},
|
|
441
617
|
},
|
|
442
618
|
async execute(ctx, input) {
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
619
|
+
const name = input.name?.slice(0, 64);
|
|
620
|
+
const origin = input.origin ?? input.outpoint;
|
|
621
|
+
const tags = [
|
|
622
|
+
'opns',
|
|
623
|
+
`type:${OPNS_CONTENT_TYPE}`,
|
|
624
|
+
`origin:${origin}`,
|
|
625
|
+
...(name ? [`name:${name}`] : []),
|
|
626
|
+
];
|
|
627
|
+
return buyOrdinal.execute(ctx, {
|
|
628
|
+
outpoint: input.outpoint,
|
|
451
629
|
inputBEEF: input.inputBEEF,
|
|
630
|
+
marketplaceAddress: input.marketplaceAddress,
|
|
631
|
+
marketplaceRate: input.marketplaceRate,
|
|
632
|
+
name,
|
|
633
|
+
origin,
|
|
634
|
+
contentType: OPNS_CONTENT_TYPE,
|
|
452
635
|
fundingProvider: input.fundingProvider,
|
|
636
|
+
basket: OPNS_BASKET,
|
|
637
|
+
tags,
|
|
453
638
|
});
|
|
454
639
|
},
|
|
455
640
|
};
|
|
456
|
-
/** All OpNS actions for registry (paid mine lives on 1sat.name / orchestrator). */
|
|
457
641
|
export const opnsActions = [
|
|
458
|
-
|
|
642
|
+
listOpns,
|
|
459
643
|
internalizeOpns,
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
644
|
+
registerOpns,
|
|
645
|
+
deregisterOpns,
|
|
646
|
+
sellOpns,
|
|
647
|
+
sendOpns,
|
|
648
|
+
cancelOpnsListing,
|
|
649
|
+
buyOpns,
|
|
464
650
|
];
|
|
465
651
|
//# sourceMappingURL=index.js.map
|