@1sat/actions 0.0.197 → 0.0.199

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/dist/apply/applyIntent.d.ts +7 -15
  2. package/dist/apply/applyIntent.d.ts.map +1 -1
  3. package/dist/apply/applyIntent.js +76 -25
  4. package/dist/apply/applyIntent.js.map +1 -1
  5. package/dist/apply/index.d.ts +1 -1
  6. package/dist/apply/index.d.ts.map +1 -1
  7. package/dist/apply/index.js +1 -1
  8. package/dist/apply/index.js.map +1 -1
  9. package/dist/apply/inscribeSigma.d.ts +1 -1
  10. package/dist/apply/inscribeSigma.d.ts.map +1 -1
  11. package/dist/apply/inscribeSigma.js +20 -13
  12. package/dist/apply/inscribeSigma.js.map +1 -1
  13. package/dist/apply/prepare.d.ts +12 -7
  14. package/dist/apply/prepare.d.ts.map +1 -1
  15. package/dist/apply/prepare.js +3 -21
  16. package/dist/apply/prepare.js.map +1 -1
  17. package/dist/apply/registry.d.ts +3 -2
  18. package/dist/apply/registry.d.ts.map +1 -1
  19. package/dist/apply/registry.js +7 -26
  20. package/dist/apply/registry.js.map +1 -1
  21. package/dist/apply/stampScriptTags.d.ts.map +1 -1
  22. package/dist/apply/stampScriptTags.js +8 -1
  23. package/dist/apply/stampScriptTags.js.map +1 -1
  24. package/dist/collections/index.d.ts.map +1 -1
  25. package/dist/collections/index.js +77 -51
  26. package/dist/collections/index.js.map +1 -1
  27. package/dist/constants.d.ts +1 -1
  28. package/dist/constants.d.ts.map +1 -1
  29. package/dist/constants.js +1 -1
  30. package/dist/constants.js.map +1 -1
  31. package/dist/cosign/finalize.js +1 -2
  32. package/dist/cosign/finalize.js.map +1 -1
  33. package/dist/index.d.ts +11 -2
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +10 -2
  36. package/dist/index.js.map +1 -1
  37. package/dist/inscriptions/index.d.ts.map +1 -1
  38. package/dist/inscriptions/index.js +34 -39
  39. package/dist/inscriptions/index.js.map +1 -1
  40. package/dist/locks/index.d.ts +1 -1
  41. package/dist/locks/index.d.ts.map +1 -1
  42. package/dist/locks/index.js +28 -13
  43. package/dist/locks/index.js.map +1 -1
  44. package/dist/opns/index.d.ts.map +1 -1
  45. package/dist/opns/index.js +60 -50
  46. package/dist/opns/index.js.map +1 -1
  47. package/dist/ordinals/index.d.ts +1 -2
  48. package/dist/ordinals/index.d.ts.map +1 -1
  49. package/dist/ordinals/index.js +84 -93
  50. package/dist/ordinals/index.js.map +1 -1
  51. package/dist/pipeline/index.d.ts +4 -0
  52. package/dist/pipeline/index.d.ts.map +1 -0
  53. package/dist/pipeline/index.js +4 -0
  54. package/dist/pipeline/index.js.map +1 -0
  55. package/dist/pipeline/runPipeline.d.ts +30 -0
  56. package/dist/pipeline/runPipeline.d.ts.map +1 -0
  57. package/dist/pipeline/runPipeline.js +82 -0
  58. package/dist/pipeline/runPipeline.js.map +1 -0
  59. package/dist/pipeline/spendTargets.d.ts +52 -0
  60. package/dist/pipeline/spendTargets.d.ts.map +1 -0
  61. package/dist/pipeline/spendTargets.js +87 -0
  62. package/dist/pipeline/spendTargets.js.map +1 -0
  63. package/dist/pipeline/unlockInput.d.ts +49 -0
  64. package/dist/pipeline/unlockInput.d.ts.map +1 -0
  65. package/dist/pipeline/unlockInput.js +316 -0
  66. package/dist/pipeline/unlockInput.js.map +1 -0
  67. package/dist/signing/index.d.ts +1 -1
  68. package/dist/signing/index.d.ts.map +1 -1
  69. package/dist/signing/index.js +1 -1
  70. package/dist/signing/index.js.map +1 -1
  71. package/dist/signing/sigma.d.ts +13 -17
  72. package/dist/signing/sigma.d.ts.map +1 -1
  73. package/dist/signing/sigma.js +37 -40
  74. package/dist/signing/sigma.js.map +1 -1
  75. package/dist/sweep/index.d.ts.map +1 -1
  76. package/dist/sweep/index.js +20 -17
  77. package/dist/sweep/index.js.map +1 -1
  78. package/dist/sync/syncCosignDeliveries.d.ts.map +1 -1
  79. package/dist/sync/syncCosignDeliveries.js +30 -20
  80. package/dist/sync/syncCosignDeliveries.js.map +1 -1
  81. package/dist/tokens/index.d.ts +8 -0
  82. package/dist/tokens/index.d.ts.map +1 -1
  83. package/dist/tokens/index.js +246 -174
  84. package/dist/tokens/index.js.map +1 -1
  85. package/dist/types.d.ts +16 -5
  86. package/dist/types.d.ts.map +1 -1
  87. package/dist/types.js.map +1 -1
  88. package/dist/utils/bsv21Remittance.d.ts +63 -0
  89. package/dist/utils/bsv21Remittance.d.ts.map +1 -0
  90. package/dist/utils/bsv21Remittance.js +146 -0
  91. package/dist/utils/bsv21Remittance.js.map +1 -0
  92. package/dist/utils/bsv21Remittance.test.d.ts +2 -0
  93. package/dist/utils/bsv21Remittance.test.d.ts.map +1 -0
  94. package/dist/utils/bsv21Remittance.test.js +115 -0
  95. package/dist/utils/bsv21Remittance.test.js.map +1 -0
  96. package/dist/utils/createTrackedAction.d.ts +42 -38
  97. package/dist/utils/createTrackedAction.d.ts.map +1 -1
  98. package/dist/utils/createTrackedAction.js +147 -79
  99. package/dist/utils/createTrackedAction.js.map +1 -1
  100. package/dist/utils/hasOneSatModule.d.ts +14 -0
  101. package/dist/utils/hasOneSatModule.d.ts.map +1 -0
  102. package/dist/utils/hasOneSatModule.js +31 -0
  103. package/dist/utils/hasOneSatModule.js.map +1 -0
  104. package/dist/utils/internalizeBeef.d.ts.map +1 -1
  105. package/dist/utils/internalizeBeef.js +37 -12
  106. package/dist/utils/internalizeBeef.js.map +1 -1
  107. package/dist/utils/loadBasketOutput.d.ts +1 -1
  108. package/dist/utils/loadBasketOutput.d.ts.map +1 -1
  109. package/dist/utils/loadBasketOutput.js +11 -3
  110. package/dist/utils/loadBasketOutput.js.map +1 -1
  111. package/dist/utils/moveBasket.d.ts +37 -0
  112. package/dist/utils/moveBasket.d.ts.map +1 -0
  113. package/dist/utils/moveBasket.js +115 -0
  114. package/dist/utils/moveBasket.js.map +1 -0
  115. package/dist/utils/ordinalRemittance.d.ts +33 -0
  116. package/dist/utils/ordinalRemittance.d.ts.map +1 -0
  117. package/dist/utils/ordinalRemittance.js +70 -0
  118. package/dist/utils/ordinalRemittance.js.map +1 -0
  119. package/dist/utils/ordinalSeedTags.d.ts +5 -3
  120. package/dist/utils/ordinalSeedTags.d.ts.map +1 -1
  121. package/dist/utils/ordinalSeedTags.js +34 -7
  122. package/dist/utils/ordinalSeedTags.js.map +1 -1
  123. package/dist/utils/resolveDestination.d.ts +3 -6
  124. package/dist/utils/resolveDestination.d.ts.map +1 -1
  125. package/dist/utils/resolveDestination.js +5 -1
  126. package/dist/utils/resolveDestination.js.map +1 -1
  127. package/dist/utils/signOrdinalInput.d.ts +3 -2
  128. package/dist/utils/signOrdinalInput.d.ts.map +1 -1
  129. package/dist/utils/signOrdinalInput.js +18 -13
  130. package/dist/utils/signOrdinalInput.js.map +1 -1
  131. package/dist/utils/stampBsv21OutputCi.d.ts +16 -0
  132. package/dist/utils/stampBsv21OutputCi.d.ts.map +1 -0
  133. package/dist/utils/stampBsv21OutputCi.js +90 -0
  134. package/dist/utils/stampBsv21OutputCi.js.map +1 -0
  135. package/dist/utils/stampOrdinalOutputCi.d.ts +15 -0
  136. package/dist/utils/stampOrdinalOutputCi.d.ts.map +1 -0
  137. package/dist/utils/stampOrdinalOutputCi.js +103 -0
  138. package/dist/utils/stampOrdinalOutputCi.js.map +1 -0
  139. package/dist/utils/walletMetadataCi.d.ts +23 -0
  140. package/dist/utils/walletMetadataCi.d.ts.map +1 -0
  141. package/dist/utils/walletMetadataCi.js +70 -0
  142. package/dist/utils/walletMetadataCi.js.map +1 -0
  143. package/package.json +7 -7
@@ -4,27 +4,63 @@
4
4
  * Actions for managing BSV21 tokens.
5
5
  */
6
6
  import { BSV21, OrdLock, P2MS } from '@1sat/templates';
7
- import { BSV21_DEPLOY_TAG, P1SAT_INTENTS, buildInputAssetLabel, buildTokenLabel, readAssetIdTag, } from '@1sat/types';
7
+ import { BSV21_DEPLOY_TAG, buildInputAssetLabel, buildTokenLabel, readAssetIdTag, } from '@1sat/types';
8
8
  import { parseOutpoint } from '@1sat/utils';
9
9
  import { BigNumber, LockingScript, OP, P2PKH, PublicKey, TransactionSignature, UnlockingScript, Utils, } from '@bsv/sdk';
10
10
  import { prepareP1SatArgs } from '../apply';
11
- import { BSV21_AUTH_BASKET, BSV21_BASKET, P1SAT_PROTOCOL, } from '../constants';
11
+ import { BSV21_AUTH_TAG, BSV21_BASKET, P1SAT_PROTOCOL, } from '../constants';
12
12
  import { executeTrackedAction } from '../utils/createTrackedAction';
13
13
  import { getDisplayValue } from '../utils/displayValue';
14
+ import { bsv21FieldsFromOutput, bsv21FilterTags, buildBsv21CustomInstructions, normalizeBsv21TokenId, } from '../utils/bsv21Remittance';
14
15
  import { resolveDestination } from '../utils/resolveDestination';
15
16
  import { signP2PKHInput } from '../utils/signP2PKH';
16
- /** tokenId from tags, or deploy outpoint when tagged bsv21:deploy. */
17
+ /** tokenId: CI `id` tag `bsv21:<id>` deploy outpoint. */
17
18
  function tokenIdFromOutput(o) {
18
- const idTag = o.tags?.find((t) => t.startsWith('bsv21:') && t !== BSV21_DEPLOY_TAG);
19
- if (idTag)
20
- return idTag.slice(6);
21
- if (o.tags?.includes(BSV21_DEPLOY_TAG)) {
22
- return o.outpoint.replace('.', '_');
23
- }
24
- return undefined;
19
+ return bsv21FieldsFromOutput({
20
+ tags: o.tags,
21
+ customInstructions: o.customInstructions,
22
+ outpoint: o.outpoint,
23
+ }).tokenId;
25
24
  }
26
25
  function matchesTokenId(o, tokenId) {
27
- return tokenIdFromOutput(o) === tokenId;
26
+ const id = tokenIdFromOutput(o);
27
+ if (!id)
28
+ return false;
29
+ return normalizeBsv21TokenId(id) === normalizeBsv21TokenId(tokenId);
30
+ }
31
+ function amtFromOutput(o) {
32
+ return bsv21FieldsFromOutput({
33
+ tags: o.tags,
34
+ customInstructions: o.customInstructions,
35
+ outpoint: o.outpoint,
36
+ }).amt;
37
+ }
38
+ /** Value tips only — skip pure auth rows in balances. */
39
+ function isBalanceableBsv21Output(o) {
40
+ const f = bsv21FieldsFromOutput({
41
+ tags: o.tags,
42
+ customInstructions: o.customInstructions,
43
+ outpoint: o.outpoint,
44
+ });
45
+ // Auth-only UTXOs (not deploy+mint value)
46
+ if (f.isAuth && !f.isDeploy)
47
+ return false;
48
+ const op = parseBsv21Op(o);
49
+ if (op === 'auth' || op === 'deploy+auth')
50
+ return false;
51
+ return !!f.amt && f.amt !== '0';
52
+ }
53
+ function parseBsv21Op(o) {
54
+ const ci = o.customInstructions;
55
+ if (!ci)
56
+ return undefined;
57
+ try {
58
+ const op = JSON.parse(ci).op;
59
+ return typeof op === 'string' ? op : undefined;
60
+ }
61
+ catch {
62
+ return undefined;
63
+ }
28
64
  }
29
65
  // ============================================================================
30
66
  // Internal helpers
@@ -121,14 +157,21 @@ export const getBsv21Balances = {
121
157
  const outputs = await listTokensInternal(ctx);
122
158
  const balanceMap = new Map();
123
159
  for (const o of outputs) {
124
- const tokenId = tokenIdFromOutput(o);
125
- const amtTag = o.tags?.find((t) => t.startsWith('amt:'))?.slice(4);
126
- if (!tokenId || !amtTag)
160
+ if (!isBalanceableBsv21Output(o))
161
+ continue;
162
+ const fields = bsv21FieldsFromOutput({
163
+ tags: o.tags,
164
+ customInstructions: o.customInstructions,
165
+ outpoint: o.outpoint,
166
+ });
167
+ const tokenId = fields.tokenId;
168
+ const amtStr = fields.amt;
169
+ if (!tokenId || !amtStr)
127
170
  continue;
128
- const amt = BigInt(amtTag);
129
- const dec = Number.parseInt(o.tags?.find((t) => t.startsWith('dec:'))?.slice(4) || '0', 10);
130
- const symTag = getDisplayValue(o, 'sym', 'sym');
131
- const rawIcon = o.tags?.find((t) => t.startsWith('icon:'))?.slice(5);
171
+ const amt = BigInt(amtStr);
172
+ const dec = Number.parseInt(fields.dec || '0', 10);
173
+ const symTag = fields.sym ?? getDisplayValue(o, 'sym', 'sym');
174
+ const rawIcon = fields.icon;
132
175
  const icon = rawIcon?.startsWith('_')
133
176
  ? `${tokenId.split('_')[0]}${rawIcon}`
134
177
  : rawIcon;
@@ -189,6 +232,10 @@ export const sendBsv21 = {
189
232
  required: ['amount', 'destination'],
190
233
  },
191
234
  },
235
+ validateOverlay: {
236
+ type: 'boolean',
237
+ description: 'Default true. When true, require active token + per-input overlay unspent check (hard fail). When false, spend wallet tips only.',
238
+ },
192
239
  },
193
240
  required: ['tokenId', 'recipients'],
194
241
  },
@@ -211,14 +258,32 @@ export const sendBsv21 = {
211
258
  resolved.push({ amount, destination: r.destination });
212
259
  }
213
260
  const totalAmount = resolved.reduce((sum, r) => sum + r.amount, 0n);
214
- if (!ctx.services) {
215
- return { error: 'services-required' };
261
+ const validateOverlay = input.validateOverlay !== false;
262
+ let tokenDetails;
263
+ if (validateOverlay) {
264
+ if (!ctx.services?.bsv21) {
265
+ return { error: 'services-required' };
266
+ }
267
+ try {
268
+ tokenDetails = await ctx.services.bsv21.getTokenDetails(tokenId);
269
+ }
270
+ catch (e) {
271
+ console.error('[sendBsv21] getTokenDetails failed:', e);
272
+ return { error: 'token-not-found' };
273
+ }
274
+ if (!tokenDetails.status.is_active) {
275
+ return { error: 'token-not-active' };
276
+ }
216
277
  }
217
- const tokenDetails = await ctx.services.bsv21.getTokenDetails(tokenId);
218
- if (!tokenDetails.status.is_active) {
219
- return { error: 'token-not-active' };
278
+ else if (ctx.services?.bsv21) {
279
+ // Best-effort meta/fees only — never gate the send.
280
+ try {
281
+ tokenDetails = await ctx.services.bsv21.getTokenDetails(tokenId);
282
+ }
283
+ catch {
284
+ tokenDetails = undefined;
285
+ }
220
286
  }
221
- const { fee_address, fee_per_output } = tokenDetails.status;
222
287
  const listResult = await ctx.wallet.listOutputs({
223
288
  basket: BSV21_BASKET,
224
289
  includeTags: true,
@@ -227,17 +292,14 @@ export const sendBsv21 = {
227
292
  limit: 10000,
228
293
  });
229
294
  const tokenUtxos = listResult.outputs.filter((o) => matchesTokenId(o, tokenId));
230
- // Batch-validate all candidate outpoints against the overlay
295
+ // When validateOverlay: only spend tips the overlay still lists unspent.
231
296
  const validOutpoints = new Set();
232
- let overlayValidated = false;
233
- if (ctx.services?.bsv21) {
297
+ if (validateOverlay) {
234
298
  const candidateOutpoints = tokenUtxos.map((o) => o.outpoint);
235
299
  try {
236
300
  const validated = await ctx.services.bsv21.validateOutputs(tokenId, candidateOutpoints, { unspent: true });
237
- overlayValidated = true;
238
301
  for (const v of validated) {
239
- const normalizedOutpoint = v.outpoint.replace('_', '.');
240
- validOutpoints.add(normalizedOutpoint);
302
+ validOutpoints.add(v.outpoint.replace('_', '.'));
241
303
  }
242
304
  }
243
305
  catch (e) {
@@ -250,29 +312,42 @@ export const sendBsv21 = {
250
312
  for (const utxo of tokenUtxos) {
251
313
  if (totalIn >= totalAmount)
252
314
  break;
253
- const amtTag = utxo.tags?.find((t) => t.startsWith('amt:'));
254
- if (!amtTag)
315
+ const amtStr = amtFromOutput(utxo);
316
+ if (!amtStr)
255
317
  continue;
256
- const utxoAmount = BigInt(amtTag.slice(4));
257
- if (overlayValidated && !validOutpoints.has(utxo.outpoint)) {
318
+ const utxoAmount = BigInt(amtStr);
319
+ if (validateOverlay && !validOutpoints.has(utxo.outpoint)) {
258
320
  continue;
259
321
  }
260
322
  selected.push(utxo);
261
323
  totalIn += utxoAmount;
262
324
  }
263
325
  if (totalIn < totalAmount) {
264
- return { error: 'insufficient-tokens' };
326
+ return {
327
+ error: validateOverlay
328
+ ? 'insufficient-valid-tokens'
329
+ : 'insufficient-tokens',
330
+ };
265
331
  }
266
332
  const outputs = [];
267
333
  const p2pkh = new P2PKH();
268
334
  const recipientKeyIDs = [];
269
- // Build recipient outputs
270
- const tokenTags = [
271
- `bsv21:${tokenId}`,
272
- `dec:${tokenDetails.token.dec ?? 0}`,
273
- ...(tokenDetails.token.sym ? [`sym:${tokenDetails.token.sym}`] : []),
274
- ...(tokenDetails.token.icon ? [`icon:${tokenDetails.token.icon}`] : []),
275
- ];
335
+ // Meta: overlay when present, else first selected tip CI.
336
+ const fromTip = selected[0]
337
+ ? bsv21FieldsFromOutput({
338
+ tags: selected[0].tags,
339
+ customInstructions: selected[0].customInstructions,
340
+ outpoint: selected[0].outpoint,
341
+ })
342
+ : {};
343
+ const tokenTags = bsv21FilterTags({ tokenId });
344
+ const tokenMeta = {
345
+ id: tokenId,
346
+ op: 'transfer',
347
+ sym: tokenDetails?.token.sym ?? fromTip.sym,
348
+ dec: tokenDetails?.token.dec ?? fromTip.dec ?? 0,
349
+ icon: tokenDetails?.token.icon ?? fromTip.icon,
350
+ };
276
351
  for (const r of resolved) {
277
352
  const recipientResolved = await resolveDestination(ctx, r.destination, {
278
353
  protocolID: P1SAT_PROTOCOL,
@@ -280,7 +355,7 @@ export const sendBsv21 = {
280
355
  });
281
356
  recipientKeyIDs.push(recipientResolved.customInstructions?.keyID);
282
357
  const transferScript = BSV21.transfer(tokenId, r.amount).lock(recipientResolved.lockingScript);
283
- // Self destinations stay in the BSV21 basket with full tags + id:.
358
+ // Self destinations stay in the BSV21 basket with filter tags + CI.
284
359
  const isSelf = r.destination.counterparty === 'self' ||
285
360
  recipientResolved.customInstructions?.counterparty === 'self';
286
361
  outputs.push({
@@ -289,20 +364,18 @@ export const sendBsv21 = {
289
364
  outputDescription: `Send ${r.amount} tokens`,
290
365
  ...(isSelf && {
291
366
  basket: BSV21_BASKET,
292
- tags: [...tokenTags, `amt:${r.amount}`],
367
+ tags: tokenTags,
293
368
  // Only when the wallet actually derived the key. A literal
294
369
  // address or lockingScript destination has no derivation, and
295
370
  // inventing a keyID here would record a triple that was never
296
371
  // used to lock this output — leaving it unspendable from its
297
372
  // own record.
298
373
  ...(recipientResolved.customInstructions?.keyID && {
299
- customInstructions: JSON.stringify({
374
+ customInstructions: buildBsv21CustomInstructions({
375
+ token: { ...tokenMeta, amt: String(r.amount) },
300
376
  protocolID: P1SAT_PROTOCOL,
301
377
  keyID: recipientResolved.customInstructions.keyID,
302
378
  counterparty: 'self',
303
- ...(tokenDetails.token.sym && {
304
- sym: tokenDetails.token.sym,
305
- }),
306
379
  }),
307
380
  }),
308
381
  }),
@@ -329,34 +402,30 @@ export const sendBsv21 = {
329
402
  satoshis: 1,
330
403
  outputDescription: 'Token change',
331
404
  basket: BSV21_BASKET,
332
- tags: [
333
- `bsv21:${tokenId}`,
334
- `amt:${change}`,
335
- `dec:${tokenDetails.token.dec ?? 0}`,
336
- ...(tokenDetails.token.sym
337
- ? [`sym:${tokenDetails.token.sym}`]
338
- : []),
339
- ...(tokenDetails.token.icon
340
- ? [`icon:${tokenDetails.token.icon}`]
341
- : []),
342
- ],
343
- customInstructions: JSON.stringify({
405
+ tags: tokenTags,
406
+ customInstructions: buildBsv21CustomInstructions({
407
+ token: { ...tokenMeta, amt: String(change) },
344
408
  protocolID: P1SAT_PROTOCOL,
345
409
  keyID: changeKeyID,
346
- ...(tokenDetails.token.sym && {
347
- sym: tokenDetails.token.sym,
348
- }),
410
+ counterparty: 'self',
349
411
  }),
350
412
  });
351
413
  }
352
- // Fee output to overlay fund address (per token output)
353
- outputs.push({
354
- lockingScript: p2pkh.lock(fee_address).toHex(),
355
- satoshis: fee_per_output * tokenOutputCount,
356
- outputDescription: 'Overlay processing fee',
357
- tags: ['fee:overlay'],
358
- });
359
- const symbol = tokenDetails.token.sym || tokenId.slice(0, 8);
414
+ // Fee out when we have fund params (required path always has details).
415
+ const fee_address = tokenDetails?.status?.fee_address;
416
+ const fee_per_output = tokenDetails?.status?.fee_per_output;
417
+ if (typeof fee_address === 'string' &&
418
+ fee_address &&
419
+ typeof fee_per_output === 'number' &&
420
+ fee_per_output > 0) {
421
+ outputs.push({
422
+ lockingScript: p2pkh.lock(fee_address).toHex(),
423
+ satoshis: fee_per_output * tokenOutputCount,
424
+ outputDescription: 'Overlay processing fee',
425
+ tags: ['fee:overlay'],
426
+ });
427
+ }
428
+ const symbol = tokenMeta.sym || tokenId.slice(0, 8);
360
429
  let inputBEEF = listResult.BEEF;
361
430
  if (!inputBEEF || inputBEEF.length === 0) {
362
431
  if (!ctx.services)
@@ -386,20 +455,19 @@ export const sendBsv21 = {
386
455
  })),
387
456
  outputs,
388
457
  options: { randomizeOutputs: false },
389
- }, P1SAT_INTENTS.BSV21_TRANSFER);
390
- const result = await executeTrackedAction(ctx.wallet, sendArgs, input.fundingProvider, inputBEEF, async (tx) => {
391
- const spends = {};
392
- for (let i = 0; i < selected.length; i++) {
393
- const utxo = selected[i];
394
- if (!utxo.customInstructions)
395
- continue;
396
- const { protocolID, keyID, counterparty } = JSON.parse(utxo.customInstructions);
397
- const unlocking = await signP2PKHInput(ctx, tx, i, protocolID, keyID, counterparty);
398
- if (typeof unlocking !== 'string')
399
- throw new Error(unlocking.error);
400
- spends[i] = { unlockingScript: unlocking };
401
- }
402
- return spends;
458
+ });
459
+ const spends = selected
460
+ .map((o) => {
461
+ const id = readAssetIdTag(o.tags);
462
+ return id
463
+ ? ({ basket: BSV21_BASKET, id })
464
+ : null;
465
+ })
466
+ .filter((t) => !!t);
467
+ const result = await executeTrackedAction(ctx.wallet, sendArgs, input.fundingProvider, inputBEEF, undefined, {
468
+ spends,
469
+ usePermissionModule: input.usePermissionModule ?? input.useOneSatModule ?? input.useModule,
470
+ permissionScheme: 'bsv21',
403
471
  });
404
472
  // Submit to overlay service for indexing
405
473
  if (result.tx && ctx.services) {
@@ -540,21 +608,19 @@ export const buyBsv21 = {
540
608
  satoshis: 1,
541
609
  outputDescription: 'Purchased tokens',
542
610
  basket: BSV21_BASKET,
543
- tags: [
544
- `bsv21:${tokenId}`,
545
- `amt:${tokenAmount}`,
546
- `dec:${tokenDetails.token.dec ?? 0}`,
547
- ...(tokenDetails.token.sym ? [`sym:${tokenDetails.token.sym}`] : []),
548
- ...(tokenDetails.token.icon
549
- ? [`icon:${tokenDetails.token.icon}`]
550
- : []),
551
- ],
552
- customInstructions: JSON.stringify({
611
+ tags: bsv21FilterTags({ tokenId }),
612
+ customInstructions: buildBsv21CustomInstructions({
613
+ token: {
614
+ id: tokenId,
615
+ amt: String(tokenAmount),
616
+ op: 'transfer',
617
+ sym: tokenDetails.token.sym,
618
+ dec: tokenDetails.token.dec ?? 0,
619
+ icon: tokenDetails.token.icon,
620
+ },
553
621
  protocolID: P1SAT_PROTOCOL,
554
622
  keyID: bsv21KeyID,
555
- ...(tokenDetails.token.sym && {
556
- sym: tokenDetails.token.sym,
557
- }),
623
+ counterparty: 'self',
558
624
  }),
559
625
  });
560
626
  const payoutReader = new Utils.Reader(ordLockData.payout);
@@ -602,10 +668,11 @@ export const buyBsv21 = {
602
668
  ],
603
669
  outputs,
604
670
  options: { randomizeOutputs: false },
605
- }, P1SAT_INTENTS.BSV21_PURCHASE);
606
- const result = await executeTrackedAction(ctx.wallet, buyArgs, input.fundingProvider, beefBinary, async (tx) => {
607
- const unlockingScript = await buildPurchaseUnlockingScript(tx, 0, listingOutput.satoshis ?? 1, listingOutput.lockingScript);
608
- return { 0: { unlockingScript: unlockingScript.toHex() } };
671
+ });
672
+ const result = await executeTrackedAction(ctx.wallet, buyArgs, input.fundingProvider, beefBinary, undefined, {
673
+ spends: [{ outpoint, scheme: 'bsv21' }],
674
+ usePermissionModule: input.usePermissionModule ?? input.useOneSatModule ?? input.useModule,
675
+ permissionScheme: 'bsv21',
609
676
  });
610
677
  // Submit to overlay service for indexing
611
678
  if (result.tx && ctx.services) {
@@ -658,25 +725,32 @@ export const buyBsv21 = {
658
725
  },
659
726
  };
660
727
  /**
661
- * Single createAction deploy. Tags include `bsv21:deploy` + metadata;
662
- * tokenId is the resulting outpoint (`txid_0`). Balance queries treat
663
- * deploy rows as that token when outpoint matches.
728
+ * Deploy via createAction. Tags: `bsv21:deploy` (+ auth). No `bsv21:<tokenId>`
729
+ * (id = this outpoint, known only after the tx). Balance/selection resolve id
730
+ * from outpoint when that tag is present.
664
731
  */
665
732
  async function executeBsv21Deploy(args) {
666
733
  const { ctx, symbol, deployScript, basket } = args;
734
+ // Deploy filing: `bsv21:deploy` (+ optional auth). No `bsv21:<tokenId>`
735
+ // (id = outpoint after mine). Never amt/sym/dec/icon tags.
736
+ const tags = args.buildTags().filter((t) => t === BSV21_DEPLOY_TAG || t === BSV21_AUTH_TAG);
737
+ if (!tags.includes(BSV21_DEPLOY_TAG))
738
+ tags.push(BSV21_DEPLOY_TAG);
667
739
  const customInstructions = args.destinationCustomInstructions
668
- ? JSON.stringify({
740
+ ? buildBsv21CustomInstructions({
741
+ token: {
742
+ // No `id` — readers use deploy outpoint as token id.
743
+ amt: args.token.amt,
744
+ op: args.token.op,
745
+ sym: symbol,
746
+ dec: args.token.dec,
747
+ icon: args.token.icon,
748
+ },
669
749
  protocolID: args.destinationCustomInstructions.protocolID,
670
750
  keyID: args.destinationCustomInstructions.keyID,
671
- ...(args.destinationCustomInstructions.counterparty !== undefined && {
672
- counterparty: args.destinationCustomInstructions.counterparty,
673
- }),
674
- sym: symbol,
751
+ counterparty: args.destinationCustomInstructions.counterparty,
675
752
  })
676
753
  : undefined;
677
- const tags = args.buildTags();
678
- if (!tags.includes(BSV21_DEPLOY_TAG))
679
- tags.push(BSV21_DEPLOY_TAG);
680
754
  const caArgs = {
681
755
  description: args.description,
682
756
  outputs: [
@@ -691,7 +765,7 @@ async function executeBsv21Deploy(args) {
691
765
  ],
692
766
  options: { randomizeOutputs: false },
693
767
  };
694
- const prepared = await prepareP1SatArgs(ctx, caArgs, P1SAT_INTENTS.BSV21_DEPLOY);
768
+ const prepared = await prepareP1SatArgs(ctx, caArgs);
695
769
  const result = await executeTrackedAction(ctx.wallet, prepared, args.fundingProvider);
696
770
  if (!result.txid) {
697
771
  return { error: 'deploy-no-txid' };
@@ -754,17 +828,13 @@ export const deployBsv21Mint = {
754
828
  basket: BSV21_BASKET,
755
829
  description: `Deploy ${symbol} (${amount} fixed supply)`,
756
830
  outputDescription: `Deploy ${symbol}`,
757
- buildTags: () => {
758
- const tags = [
759
- BSV21_DEPLOY_TAG,
760
- `amt:${amount}`,
761
- `dec:${decimals}`,
762
- `sym:${symbol}`,
763
- ];
764
- if (icon)
765
- tags.push(`icon:${icon}`);
766
- return tags;
831
+ token: {
832
+ amt: String(amount),
833
+ op: 'deploy+mint',
834
+ dec: decimals,
835
+ icon,
767
836
  },
837
+ buildTags: () => [BSV21_DEPLOY_TAG],
768
838
  });
769
839
  if (result.error)
770
840
  return { error: result.error };
@@ -861,15 +931,16 @@ export const deployBsv21Auth = {
861
931
  symbol,
862
932
  deployScript,
863
933
  destinationCustomInstructions: resolved.customInstructions,
864
- basket: BSV21_AUTH_BASKET,
934
+ basket: BSV21_BASKET,
865
935
  description: `Deploy ${symbol} (mintable)`,
866
936
  outputDescription: `Deploy ${symbol} auth`,
867
- buildTags: () => {
868
- const tags = [BSV21_DEPLOY_TAG, `dec:${decimals}`, `sym:${symbol}`];
869
- if (icon)
870
- tags.push(`icon:${icon}`);
871
- return tags;
937
+ token: {
938
+ amt: '0',
939
+ op: 'deploy+auth',
940
+ dec: decimals,
941
+ icon,
872
942
  },
943
+ buildTags: () => [BSV21_DEPLOY_TAG, BSV21_AUTH_TAG],
873
944
  });
874
945
  if (result.error)
875
946
  return { error: result.error };
@@ -893,7 +964,7 @@ export const deployBsv21Auth = {
893
964
  index: 0,
894
965
  protocolID: P1SAT_PROTOCOL,
895
966
  keyID: resolved.customInstructions?.keyID,
896
- basket: BSV21_AUTH_BASKET,
967
+ basket: BSV21_BASKET,
897
968
  satoshis: 1,
898
969
  },
899
970
  ],
@@ -976,15 +1047,17 @@ export const mintBsv21 = {
976
1047
  }
977
1048
  // Look up the token's metadata for tag enrichment.
978
1049
  const tokenDetails = await ctx.services.bsv21.getTokenDetails(tokenId);
979
- // Find a spendable auth UTXO for this token.
1050
+ // Find a spendable auth UTXO for this token (same basket as balance).
980
1051
  const authList = await ctx.wallet.listOutputs({
981
- basket: BSV21_AUTH_BASKET,
1052
+ basket: BSV21_BASKET,
1053
+ tags: [BSV21_AUTH_TAG, `bsv21:${tokenId}`],
1054
+ tagQueryMode: 'all',
982
1055
  includeTags: true,
983
1056
  includeCustomInstructions: true,
984
1057
  include: 'entire transactions',
985
1058
  limit: 1000,
986
1059
  });
987
- const authUtxo = authList.outputs.find((o) => o.tags?.includes(`bsv21:${tokenId}`));
1060
+ const authUtxo = authList.outputs[0];
988
1061
  if (!authUtxo) {
989
1062
  return { error: 'no-auth-utxo-for-token' };
990
1063
  }
@@ -1001,30 +1074,29 @@ export const mintBsv21 = {
1001
1074
  keyIDPrefix: `bsv21-mint-${tokenId}`,
1002
1075
  });
1003
1076
  const mintScript = BSV21.mint(tokenId, mintAmount).lock(mintResolved.lockingScript);
1004
- const mintTags = [
1005
- `bsv21:${tokenId}`,
1006
- `amt:${mintAmount}`,
1007
- `dec:${tokenDetails.token.dec ?? 0}`,
1008
- ...(tokenDetails.token.sym ? [`sym:${tokenDetails.token.sym}`] : []),
1009
- ...(tokenDetails.token.icon
1010
- ? [`icon:${tokenDetails.token.icon}`]
1011
- : []),
1012
- ];
1013
- const mintCI = mintResolved.customInstructions
1014
- ? JSON.stringify({
1015
- ...mintResolved.customInstructions,
1016
- ...(tokenDetails.token.sym && { sym: tokenDetails.token.sym }),
1017
- })
1018
- : tokenDetails.token.sym
1019
- ? JSON.stringify({ sym: tokenDetails.token.sym })
1020
- : undefined;
1077
+ const mintMeta = {
1078
+ id: tokenId,
1079
+ amt: String(mintAmount),
1080
+ op: 'mint',
1081
+ sym: tokenDetails.token.sym,
1082
+ dec: tokenDetails.token.dec ?? 0,
1083
+ icon: tokenDetails.token.icon,
1084
+ };
1021
1085
  outputs.push({
1022
1086
  lockingScript: mintScript.toHex(),
1023
1087
  satoshis: 1,
1024
1088
  outputDescription: `Mint ${mintAmount} tokens`,
1025
1089
  basket: BSV21_BASKET,
1026
- tags: mintTags,
1027
- customInstructions: mintCI,
1090
+ tags: bsv21FilterTags({ tokenId }),
1091
+ customInstructions: mintResolved.customInstructions
1092
+ ? buildBsv21CustomInstructions({
1093
+ token: mintMeta,
1094
+ protocolID: mintResolved.customInstructions.protocolID,
1095
+ keyID: mintResolved.customInstructions.keyID,
1096
+ counterparty: mintResolved.customInstructions
1097
+ .counterparty,
1098
+ })
1099
+ : buildBsv21CustomInstructions({ token: mintMeta }),
1028
1100
  });
1029
1101
  }
1030
1102
  if (!endMinting) {
@@ -1033,30 +1105,30 @@ export const mintBsv21 = {
1033
1105
  keyIDPrefix: `bsv21-auth-${tokenId}`,
1034
1106
  });
1035
1107
  const authScript = BSV21.auth(tokenId).lock(authResolved.lockingScript);
1036
- const authTags = [
1037
- `bsv21:${tokenId}`,
1038
- `dec:${tokenDetails.token.dec ?? 0}`,
1039
- ...(tokenDetails.token.sym ? [`sym:${tokenDetails.token.sym}`] : []),
1040
- ...(tokenDetails.token.icon
1041
- ? [`icon:${tokenDetails.token.icon}`]
1042
- : []),
1043
- ];
1044
- const authCustomInstructions = authResolved.customInstructions
1045
- ? JSON.stringify({
1046
- ...authResolved.customInstructions,
1047
- ...(tokenDetails.token.sym && { sym: tokenDetails.token.sym }),
1048
- })
1049
- : tokenDetails.token.sym
1050
- ? JSON.stringify({ sym: tokenDetails.token.sym })
1051
- : undefined;
1108
+ const authMeta = {
1109
+ id: tokenId,
1110
+ amt: '0',
1111
+ op: 'auth',
1112
+ sym: tokenDetails.token.sym,
1113
+ dec: tokenDetails.token.dec ?? 0,
1114
+ icon: tokenDetails.token.icon,
1115
+ };
1052
1116
  authOutputIndex = outputs.length;
1053
1117
  outputs.push({
1054
1118
  lockingScript: authScript.toHex(),
1055
1119
  satoshis: 1,
1056
1120
  outputDescription: 'Continuing mint authority',
1057
- basket: BSV21_AUTH_BASKET,
1058
- tags: authTags,
1059
- customInstructions: authCustomInstructions,
1121
+ basket: BSV21_BASKET,
1122
+ tags: bsv21FilterTags({ tokenId, auth: true }),
1123
+ customInstructions: authResolved.customInstructions
1124
+ ? buildBsv21CustomInstructions({
1125
+ token: authMeta,
1126
+ protocolID: authResolved.customInstructions.protocolID,
1127
+ keyID: authResolved.customInstructions.keyID,
1128
+ counterparty: authResolved.customInstructions
1129
+ .counterparty,
1130
+ })
1131
+ : buildBsv21CustomInstructions({ token: authMeta }),
1060
1132
  });
1061
1133
  }
1062
1134
  // Optional fee output to overlay fund address (per token output).
@@ -1086,7 +1158,7 @@ export const mintBsv21 = {
1086
1158
  labels: [
1087
1159
  buildTokenLabel(tokenId),
1088
1160
  ...(authInputId
1089
- ? [buildInputAssetLabel(BSV21_AUTH_BASKET, authInputId)]
1161
+ ? [buildInputAssetLabel(BSV21_BASKET, authInputId)]
1090
1162
  : []),
1091
1163
  ],
1092
1164
  inputBEEF,
@@ -1099,7 +1171,7 @@ export const mintBsv21 = {
1099
1171
  ],
1100
1172
  outputs,
1101
1173
  options: { randomizeOutputs: false },
1102
- }, P1SAT_INTENTS.BSV21_MINT);
1174
+ });
1103
1175
  const result = await executeTrackedAction(ctx.wallet, mintArgs, input.fundingProvider, inputBEEF, async (tx) => {
1104
1176
  if (authCI.multiSig === true) {
1105
1177
  // 1-of-N P2MS auth: this wallet contributes its single