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