@neuraiproject/neurai-assets 1.5.2 → 1.5.3

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.
@@ -5999,66 +5999,116 @@ var NeuraiAssetsBundle = (function (exports) {
5999
5999
  }
6000
6000
 
6001
6001
  /**
6002
- * Bytes an asset payload adds on top of a plain destination output.
6003
- *
6004
- * An asset output is `<destination script> OP_XNA_ASSET <pushdata payload>
6005
- * OP_DROP`, so it costs the destination plus the wrapper (OP_XNA_ASSET,
6006
- * the pushdata prefix and OP_DROP) plus the payload itself:
6002
+ * Encoders that produce the exact scriptPubKey the node will see.
6007
6003
  *
6008
- * marker(3) + type(1) + nameLength(1) + name + kind-specific tail
6004
+ * Sizing asset outputs from a hand-written byte formula drifted: the owner
6005
+ * token a reissue RETURNS is serialized as a transfer (it carries an amount),
6006
+ * not as the owner payload an issuance CREATES, and the null-asset-data
6007
+ * outputs of tag/freeze were not counted at all — those outputs are not
6008
+ * P2PKH-plus-payload, they replace the destination script entirely. The result
6009
+ * was twelve of eighteen operations budgeting below what the node charges.
6009
6010
  *
6010
- * Sizing these as bare P2PKH outputs under-counts a transaction by tens to
6011
- * hundreds of bytes. That is invisible while the node's fee rate sits well
6012
- * above its minimum relay fee, and becomes `min relay fee not met` as soon as
6013
- * it does not — which is exactly the failure this file's header warns about.
6014
- *
6015
- * @param {object} descriptor - Output descriptor with `assetName` and `kind`
6016
- * @returns {number} Extra bytes, or 0 when the output carries no asset payload
6011
+ * Asking the serializer is the only way to keep this from drifting again: the
6012
+ * numbers below are not a model of the encoding, they ARE the encoding.
6017
6013
  */
6018
- function assetPayloadBytes(descriptor) {
6019
- if (!descriptor || typeof descriptor !== 'object' || !descriptor.assetName) {
6020
- return 0;
6021
- }
6014
+ const ct = requireDist_1();
6022
6015
 
6023
- // One byte per character, matching how the payload encodes the name
6024
- // (`serializeString` -> `asciiBytes`, which writes a single byte per char).
6025
- // Node's byte-length helper would be equivalent here, but it hangs off a
6026
- // global that browsers do not have: using it broke the extension bundle with
6027
- // "Buffer is not defined", and this library does much of its work there.
6028
- const nameLength = String(descriptor.assetName).length;
6029
- const kind = descriptor.kind || 'transfer';
6016
+ /** Bytes a CompactSize length prefix occupies for `n`. */
6017
+ function compactSizeBytes(n) {
6018
+ if (n < 253) return 1;
6019
+ if (n <= 0xffff) return 3;
6020
+ if (n <= 0xffffffff) return 5;
6021
+ return 9;
6022
+ }
6030
6023
 
6031
- // marker(3) + type(1) + CompactSize name length(1) + name
6032
- let payload = 5 + nameLength;
6024
+ /** An IPFS hash of the right LENGTH; only its size matters here. */
6025
+ const IPFS_PLACEHOLDER = 'Qm' + 'a'.repeat(44);
6026
+
6027
+ /**
6028
+ * The exact scriptPubKey for an asset-bearing output descriptor, or null when
6029
+ * the descriptor names no asset operation.
6030
+ *
6031
+ * Values are placeholders on purpose: every field the amount or the flag lands
6032
+ * in is fixed-width, so a zero costs the same bytes as the real number. The
6033
+ * name, the address and the presence of IPFS are the only things that move the
6034
+ * size, and those come from the descriptor.
6035
+ *
6036
+ * @param {object} descriptor - Output descriptor
6037
+ * @returns {Uint8Array|null} Encoded script, or null
6038
+ */
6039
+ function assetOutputScript(descriptor) {
6040
+ const { address, assetName, kind = 'transfer' } = descriptor;
6041
+ const ipfs = descriptor.hasIpfs ? (descriptor.ipfsHash || IPFS_PLACEHOLDER) : undefined;
6033
6042
 
6034
6043
  switch (kind) {
6035
6044
  case 'owner':
6036
- // The owner payload carries no amount: it is always exactly one unit.
6037
- break;
6045
+ return ct.encodeOwnerAssetScript(address, assetName);
6038
6046
  case 'issue':
6039
- // amount(8) + units(1) + reissuable(1) + has_ipfs(1)
6040
- payload += 11;
6041
- break;
6047
+ return ct.encodeNewAssetScript(address, assetName, 0n, 0, true, ipfs);
6042
6048
  case 'reissue':
6043
- // amount(8) + units(1) + reissuable(1)
6044
- payload += 10;
6045
- break;
6049
+ return ct.encodeReissueAssetScript(address, assetName, 0n, undefined, true, ipfs);
6050
+ case 'tag':
6051
+ return ct.encodeNullAssetTagScript(address, assetName, 'tag');
6052
+ case 'restriction':
6053
+ return ct.encodeNullAssetRestrictionScript(address, assetName, 1);
6054
+ case 'globalRestriction':
6055
+ return ct.encodeGlobalRestrictionScript(assetName, 1);
6056
+ case 'verifier':
6057
+ return ct.encodeVerifierStringScript(descriptor.verifierString || '');
6058
+ case 'transfer':
6059
+ return ct.encodeAssetTransferScript(address, assetName, 0n);
6046
6060
  default:
6047
- // transfer: amount(8)
6048
- payload += 8;
6049
- break;
6061
+ return null;
6050
6062
  }
6063
+ }
6051
6064
 
6052
- if (descriptor.hasIpfs) {
6053
- payload += 34;
6054
- }
6065
+ /**
6066
+ * Kinds whose script REPLACES the destination rather than extending it.
6067
+ *
6068
+ * A tag, a restriction or a verifier string is not "a payment with a payload
6069
+ * bolted on": there is no P2PKH to pay. Adding a destination's bytes to these
6070
+ * over-counts; treating them as plain destinations, which is what the builders
6071
+ * used to do, under-counts by far more.
6072
+ */
6073
+ const STANDALONE_KINDS = new Set(['tag', 'restriction', 'globalRestriction', 'verifier']);
6055
6074
 
6056
- // OP_XNA_ASSET(1) + pushdata prefix + OP_DROP(1). Payloads over 75 bytes
6057
- // need OP_PUSHDATA1, which is one byte wider reachable with the 121-char
6058
- // asset names testnet and regtest allow for DePIN.
6059
- const pushPrefix = payload > 75 ? 2 : 1;
6075
+ /**
6076
+ * Fallback used only when the encoders cannot express a descriptor an
6077
+ * address family they do not accept, say. Keeps the previous behaviour rather
6078
+ * than throwing in the middle of a fee estimate.
6079
+ */
6080
+ function assetPayloadBytesApprox(descriptor) {
6081
+ const nameLength = String(descriptor.assetName || '').length;
6082
+ const kind = descriptor.kind || 'transfer';
6083
+ let payload = 5 + nameLength;
6084
+ if (kind === 'issue') payload += 11;
6085
+ else if (kind === 'reissue') payload += 10;
6086
+ else if (kind !== 'owner') payload += 8;
6087
+ if (descriptor.hasIpfs) payload += 34;
6088
+ return 1 + (payload > 75 ? 2 : 1) + payload + 1;
6089
+ }
6060
6090
 
6061
- return 1 + pushPrefix + payload + 1;
6091
+ /**
6092
+ * Bytes an asset payload adds on top of a plain destination output.
6093
+ *
6094
+ * Kept for callers that only want the delta. Standalone kinds have no
6095
+ * destination to add to, so this is not meaningful for them.
6096
+ *
6097
+ * @param {object} descriptor - Output descriptor with `assetName` and `kind`
6098
+ * @returns {number} Extra bytes, or 0 when the output carries no asset payload
6099
+ */
6100
+ function assetPayloadBytes(descriptor) {
6101
+ if (!descriptor || typeof descriptor !== 'object' || !descriptor.assetName) {
6102
+ return 0;
6103
+ }
6104
+ try {
6105
+ const script = assetOutputScript(descriptor);
6106
+ if (!script) return 0;
6107
+ const base = isPQAddress(descriptor.address) ? 34 : 25;
6108
+ return script.length - base;
6109
+ } catch {
6110
+ return assetPayloadBytesApprox(descriptor);
6111
+ }
6062
6112
  }
6063
6113
 
6064
6114
  /**
@@ -6072,10 +6122,26 @@ var NeuraiAssetsBundle = (function (exports) {
6072
6122
  * @returns {number} Estimated bytes
6073
6123
  */
6074
6124
  function estimateOutputBytes(target) {
6125
+ if (typeof target !== 'string' && target && (target.assetName || target.kind === 'verifier')) {
6126
+ try {
6127
+ const script = assetOutputScript(target);
6128
+ if (script) {
6129
+ // value(8) + CompactSize(scriptLen) + script
6130
+ return 8 + compactSizeBytes(script.length) + script.length;
6131
+ }
6132
+ } catch {
6133
+ // fall through to the approximation
6134
+ }
6135
+ if (STANDALONE_KINDS.has(target.kind)) {
6136
+ return VBYTES.legacyOutputBytes;
6137
+ }
6138
+ const base = isPQAddress(target.address) ? VBYTES.pqOutputBytes : VBYTES.legacyOutputBytes;
6139
+ return base + assetPayloadBytesApprox(target);
6140
+ }
6141
+
6075
6142
  const address =
6076
6143
  typeof target === 'string' ? target : (target && target.address) || '';
6077
- const base = isPQAddress(address) ? VBYTES.pqOutputBytes : VBYTES.legacyOutputBytes;
6078
- return base + assetPayloadBytes(typeof target === 'string' ? null : target);
6144
+ return isPQAddress(address) ? VBYTES.pqOutputBytes : VBYTES.legacyOutputBytes;
6079
6145
  }
6080
6146
 
6081
6147
  /**
@@ -6105,6 +6171,8 @@ var NeuraiAssetsBundle = (function (exports) {
6105
6171
 
6106
6172
  feeSizing = {
6107
6173
  VBYTES,
6174
+ compactSizeBytes,
6175
+ assetOutputScript,
6108
6176
  isPQAddress,
6109
6177
  isPQScript,
6110
6178
  estimateInputVbytes,
@@ -9083,8 +9151,24 @@ var NeuraiAssetsBundle = (function (exports) {
9083
9151
  throw new Error('Cannot parse parent asset from SUB asset name');
9084
9152
  }
9085
9153
 
9086
- // 3. Check if parent asset exists
9087
- const parentExists = await this.assetExists(parentAssetName);
9154
+ // El token owner no depende de nada de lo que sigue: pedirlo a la vez que
9155
+ // las lecturas del asset ahorra una ida y vuelta completa. Se ESPERA en su
9156
+ // sitio de siempre, así que el orden de los errores no cambia.
9157
+ const ownerTokenName = AssetNameParser.getOwnerTokenName(parentAssetName);
9158
+ const addresses = await this._getAddresses();
9159
+ const ownerTokenLookup = this.ownerTokenManager.findOwnerTokenUTXO(
9160
+ ownerTokenName,
9161
+ addresses
9162
+ );
9163
+ ownerTokenLookup.catch(() => {});
9164
+
9165
+ // 3-4. Existencia del padre y del sub: independientes entre sí, así que
9166
+ // se preguntan a la vez. Se comprueban en el mismo orden de antes, luego
9167
+ // «no existe el padre» sigue ganando a «el sub ya existe».
9168
+ const [parentExists, subExists] = await Promise.all([
9169
+ this.assetExists(parentAssetName),
9170
+ this.assetExists(assetName)
9171
+ ]);
9088
9172
  if (!parentExists) {
9089
9173
  throw new ParentAssetNotFoundError(
9090
9174
  `Parent asset ${parentAssetName} does not exist. You must create the ROOT asset first.`,
@@ -9092,8 +9176,6 @@ var NeuraiAssetsBundle = (function (exports) {
9092
9176
  );
9093
9177
  }
9094
9178
 
9095
- // 4. Check if SUB asset already exists
9096
- const subExists = await this.assetExists(assetName);
9097
9179
  if (subExists) {
9098
9180
  throw new AssetExistsError(
9099
9181
  `Asset ${assetName} already exists on the blockchain`,
@@ -9102,18 +9184,13 @@ var NeuraiAssetsBundle = (function (exports) {
9102
9184
  }
9103
9185
 
9104
9186
  // 5. Get addresses
9105
- const addresses = await this._getAddresses();
9106
9187
  const toAddress = await this.getToAddress();
9107
9188
  const changeAddress = await this.getChangeAddress();
9108
9189
 
9109
9190
  // 6. Find parent's owner token (CRITICAL: must have this)
9110
- const ownerTokenName = AssetNameParser.getOwnerTokenName(parentAssetName);
9111
9191
  let ownerTokenUTXO;
9112
9192
  try {
9113
- ownerTokenUTXO = await this.ownerTokenManager.findOwnerTokenUTXO(
9114
- ownerTokenName,
9115
- addresses
9116
- );
9193
+ ownerTokenUTXO = await ownerTokenLookup;
9117
9194
  } catch (error) {
9118
9195
  if (error instanceof OwnerTokenNotFoundError) {
9119
9196
  throw new OwnerTokenNotFoundError(
@@ -9134,7 +9211,11 @@ var NeuraiAssetsBundle = (function (exports) {
9134
9211
  const outputAddresses = [
9135
9212
  burnInfo.address,
9136
9213
  changeAddress,
9137
- { address: changeAddress, assetName: ownerTokenName, kind: 'owner' },
9214
+ // El token owner que la operación GASTA y devuelve viaja como
9215
+ // transferencia (lleva importe), no con el payload 'owner', que sólo
9216
+ // describe el token que una emisión CREA. Estimarlo como 'owner' dejaba
9217
+ // la transacción 8 bytes por debajo de lo que el nodo cobra.
9218
+ { address: changeAddress, assetName: ownerTokenName },
9138
9219
  { address: toAddress, assetName, kind: 'issue', hasIpfs },
9139
9220
  { address: changeAddress, assetName: `${assetName}!`, kind: 'owner' },
9140
9221
  ];
@@ -9518,6 +9599,19 @@ var NeuraiAssetsBundle = (function (exports) {
9518
9599
  newIpfs
9519
9600
  } = this.params;
9520
9601
 
9602
+ // El token owner no depende de nada de lo que sigue: pedirlo a la vez que
9603
+ // los datos del asset ahorra una ida y vuelta completa. Se ESPERA en su
9604
+ // sitio de siempre (paso 6), así que el orden de los errores no cambia:
9605
+ // «el asset no existe» y «no es reemitible» se siguen lanzando antes que
9606
+ // «no tienes el token owner».
9607
+ const ownerTokenName = AssetNameParser.getOwnerTokenName(assetName);
9608
+ const addresses = await this._getAddresses();
9609
+ const ownerTokenLookup = this.ownerTokenManager.findOwnerTokenUTXO(
9610
+ ownerTokenName,
9611
+ addresses
9612
+ );
9613
+ ownerTokenLookup.catch(() => {});
9614
+
9521
9615
  // 2. Get asset data to verify it exists and is reissuable
9522
9616
  const assetData = await this.getAssetData(assetName);
9523
9617
  if (!assetData) {
@@ -9553,19 +9647,14 @@ var NeuraiAssetsBundle = (function (exports) {
9553
9647
  }
9554
9648
 
9555
9649
  // 5. Get addresses
9556
- const addresses = await this._getAddresses();
9557
9650
  const toAddress = await this.getToAddress();
9558
9651
  const changeAddress = await this.getChangeAddress();
9559
9652
  const isDepinAsset = AssetNameParser.isDepin(assetName);
9560
9653
 
9561
9654
  // 6. Find owner token (CRITICAL: must have this)
9562
- const ownerTokenName = AssetNameParser.getOwnerTokenName(assetName);
9563
9655
  let ownerTokenUTXO;
9564
9656
  try {
9565
- ownerTokenUTXO = await this.ownerTokenManager.findOwnerTokenUTXO(
9566
- ownerTokenName,
9567
- addresses
9568
- );
9657
+ ownerTokenUTXO = await ownerTokenLookup;
9569
9658
  } catch (error) {
9570
9659
  if (error instanceof OwnerTokenNotFoundError) {
9571
9660
  throw new OwnerTokenNotFoundError(
@@ -9586,7 +9675,11 @@ var NeuraiAssetsBundle = (function (exports) {
9586
9675
  const outputAddresses = [
9587
9676
  burnInfo.address,
9588
9677
  changeAddress,
9589
- { address: changeAddress, assetName: ownerTokenName, kind: 'owner' },
9678
+ // El token owner que la operación GASTA y devuelve viaja como
9679
+ // transferencia (lleva importe), no con el payload 'owner', que sólo
9680
+ // describe el token que una emisión CREA. Estimarlo como 'owner' dejaba
9681
+ // la transacción 8 bytes por debajo de lo que el nodo cobra.
9682
+ { address: changeAddress, assetName: ownerTokenName },
9590
9683
  { address: toAddress, assetName, kind: 'reissue', hasIpfs: Boolean(newIpfs) },
9591
9684
  ];
9592
9685
  // Fund the XNA side. The owner-token input counts towards the size
@@ -9854,7 +9947,8 @@ var NeuraiAssetsBundle = (function (exports) {
9854
9947
  ...recipients.map(r => ({ address: r.address, assetName })),
9855
9948
  { address: changeAddress, assetName }, // asset change (harmless over-count if absent)
9856
9949
  ...(isDepin
9857
- ? [{ address: changeAddress, assetName: ownerTokenName, kind: 'owner' }]
9950
+ // Escolta: se gasta y se devuelve, luego es una transferencia.
9951
+ ? [{ address: changeAddress, assetName: ownerTokenName }]
9858
9952
  : []),
9859
9953
  ];
9860
9954
 
@@ -10138,6 +10232,17 @@ var NeuraiAssetsBundle = (function (exports) {
10138
10232
  ipfsHashes = []
10139
10233
  } = this.params;
10140
10234
 
10235
+ // El token owner no depende de nada de lo que sigue: pedirlo a la vez que
10236
+ // las lecturas del asset ahorra una ida y vuelta completa. Se ESPERA en su
10237
+ // sitio de siempre, así que el orden de los errores no cambia.
10238
+ const ownerTokenName = AssetNameParser.getOwnerTokenName(rootName);
10239
+ const addresses = await this._getAddresses();
10240
+ const ownerTokenLookup = this.ownerTokenManager.findOwnerTokenUTXO(
10241
+ ownerTokenName,
10242
+ addresses
10243
+ );
10244
+ ownerTokenLookup.catch(() => {});
10245
+
10141
10246
  // 2. Check if parent asset exists
10142
10247
  const parentExists = await this.assetExists(rootName);
10143
10248
  if (!parentExists) {
@@ -10147,31 +10252,31 @@ var NeuraiAssetsBundle = (function (exports) {
10147
10252
  );
10148
10253
  }
10149
10254
 
10150
- // 3. Check if any of the unique assets already exist
10151
- for (const tag of assetTags) {
10152
- const fullName = `${rootName}#${tag}`;
10153
- const exists = await this.assetExists(fullName);
10154
- if (exists) {
10155
- throw new AssetExistsError(
10156
- `Unique asset ${fullName} already exists on the blockchain`,
10157
- fullName
10158
- );
10159
- }
10255
+ // 3. Check if any of the unique assets already exist.
10256
+ // En fila, emitir diez únicos costaba diez idas y vueltas antes de
10257
+ // empezar a construir. Ninguna depende de la anterior. El resultado se
10258
+ // recorre en orden, así que el error sigue nombrando el primer nombre
10259
+ // repetido de la lista, no el que el nodo conteste primero.
10260
+ const uniqueNames = assetTags.map(tag => `${rootName}#${tag}`);
10261
+ const uniqueExists = await Promise.all(
10262
+ uniqueNames.map(fullName => this.assetExists(fullName))
10263
+ );
10264
+ const takenIndex = uniqueExists.findIndex(Boolean);
10265
+ if (takenIndex !== -1) {
10266
+ throw new AssetExistsError(
10267
+ `Unique asset ${uniqueNames[takenIndex]} already exists on the blockchain`,
10268
+ uniqueNames[takenIndex]
10269
+ );
10160
10270
  }
10161
10271
 
10162
10272
  // 4. Get addresses
10163
- const addresses = await this._getAddresses();
10164
10273
  const toAddress = await this.getToAddress();
10165
10274
  const changeAddress = await this.getChangeAddress();
10166
10275
 
10167
10276
  // 5. Find parent's owner token (CRITICAL: must have this)
10168
- const ownerTokenName = AssetNameParser.getOwnerTokenName(rootName);
10169
10277
  let ownerTokenUTXO;
10170
10278
  try {
10171
- ownerTokenUTXO = await this.ownerTokenManager.findOwnerTokenUTXO(
10172
- ownerTokenName,
10173
- addresses
10174
- );
10279
+ ownerTokenUTXO = await ownerTokenLookup;
10175
10280
  } catch (error) {
10176
10281
  if (error instanceof OwnerTokenNotFoundError) {
10177
10282
  throw new OwnerTokenNotFoundError(
@@ -10193,7 +10298,11 @@ var NeuraiAssetsBundle = (function (exports) {
10193
10298
  const outputAddresses = [
10194
10299
  burnInfo.address,
10195
10300
  changeAddress,
10196
- { address: changeAddress, assetName: ownerTokenName, kind: 'owner' },
10301
+ // El token owner que la operación GASTA y devuelve viaja como
10302
+ // transferencia (lleva importe), no con el payload 'owner', que sólo
10303
+ // describe el token que una emisión CREA. Estimarlo como 'owner' dejaba
10304
+ // la transacción 8 bytes por debajo de lo que el nodo cobra.
10305
+ { address: changeAddress, assetName: ownerTokenName },
10197
10306
  ...assetTags.map(tag => ({
10198
10307
  address: toAddress,
10199
10308
  assetName: `${rootName}#${tag}`,
@@ -10677,6 +10786,17 @@ var NeuraiAssetsBundle = (function (exports) {
10677
10786
  ipfsHash = ''
10678
10787
  } = this.params;
10679
10788
 
10789
+ // El token owner no depende de nada de lo que sigue: pedirlo a la vez que
10790
+ // las lecturas del asset ahorra una ida y vuelta completa. Se ESPERA en su
10791
+ // sitio de siempre, así que el orden de los errores no cambia.
10792
+ const ownerTokenName = AssetNameParser.getOwnerTokenName(assetName);
10793
+ const addresses = await this._getAddresses();
10794
+ const ownerTokenLookup = this.ownerTokenManager.findOwnerTokenUTXO(
10795
+ ownerTokenName,
10796
+ addresses
10797
+ );
10798
+ ownerTokenLookup.catch(() => {});
10799
+
10680
10800
  // 2. Check if asset already exists
10681
10801
  const exists = await this.assetExists(assetName);
10682
10802
  if (exists) {
@@ -10693,18 +10813,13 @@ var NeuraiAssetsBundle = (function (exports) {
10693
10813
  const burnInfo = this.burnManager.getIssueRestrictedBurn();
10694
10814
 
10695
10815
  // 5. Get addresses
10696
- const addresses = await this._getAddresses();
10697
10816
  const toAddress = await this.getToAddress();
10698
10817
  const changeAddress = await this.getChangeAddress();
10699
10818
 
10700
10819
  // 6. Find owner token UTXO (CRITICAL: node requires it as input)
10701
- const ownerTokenName = AssetNameParser.getOwnerTokenName(assetName);
10702
10820
  let ownerTokenUTXO;
10703
10821
  try {
10704
- ownerTokenUTXO = await this.ownerTokenManager.findOwnerTokenUTXO(
10705
- ownerTokenName,
10706
- addresses
10707
- );
10822
+ ownerTokenUTXO = await ownerTokenLookup;
10708
10823
  } catch (error) {
10709
10824
  if (error instanceof OwnerTokenNotFoundError) {
10710
10825
  throw new OwnerTokenNotFoundError(
@@ -10716,11 +10831,16 @@ var NeuraiAssetsBundle = (function (exports) {
10716
10831
  }
10717
10832
 
10718
10833
  // 7. Estimate fee (+1 for owner token input)
10834
+ // Las salidas de asset deben describirse como lo que el nodo serializa.
10835
+ // Como direcciones desnudas se contaban 34 bytes por salida y el payload
10836
+ // entero quedaba sin pagar; las de datos nulos (tag, congelación,
10837
+ // verificador) ni siquiera llevan destino: su script SUSTITUYE al P2PKH.
10719
10838
  const outputAddresses = [
10720
10839
  burnInfo.address,
10721
10840
  changeAddress,
10722
- changeAddress, // owner token return goes to change address
10723
- toAddress,
10841
+ { kind: 'verifier', assetName, verifierString },
10842
+ { address: changeAddress, assetName: ownerTokenName },
10843
+ { address: toAddress, assetName, kind: 'issue', hasIpfs: Boolean(this.params.ipfsHash) },
10724
10844
  ];
10725
10845
  // Fund the XNA side. The owner-token input counts towards the size
10726
10846
  // estimate from the first round and is excluded from XNA selection.
@@ -10930,6 +11050,17 @@ var NeuraiAssetsBundle = (function (exports) {
10930
11050
  newIpfs
10931
11051
  } = this.params;
10932
11052
 
11053
+ // El token owner no depende de nada de lo que sigue: pedirlo a la vez que
11054
+ // las lecturas del asset ahorra una ida y vuelta completa. Se ESPERA en su
11055
+ // sitio de siempre, así que el orden de los errores no cambia.
11056
+ const ownerTokenName = AssetNameParser.getOwnerTokenName(assetName);
11057
+ const addresses = await this._getAddresses();
11058
+ const ownerTokenLookup = this.ownerTokenManager.findOwnerTokenUTXO(
11059
+ ownerTokenName,
11060
+ addresses
11061
+ );
11062
+ ownerTokenLookup.catch(() => {});
11063
+
10933
11064
  // 2. Get asset data to verify it exists and is reissuable
10934
11065
  const assetData = await this.getAssetData(assetName);
10935
11066
  if (!assetData) {
@@ -10965,18 +11096,13 @@ var NeuraiAssetsBundle = (function (exports) {
10965
11096
  }
10966
11097
 
10967
11098
  // 5. Get addresses
10968
- const addresses = await this._getAddresses();
10969
11099
  const toAddress = await this.getToAddress();
10970
11100
  const changeAddress = await this.getChangeAddress();
10971
11101
 
10972
11102
  // 6. Find owner token (CRITICAL: must have this)
10973
- const ownerTokenName = AssetNameParser.getOwnerTokenName(assetName);
10974
11103
  let ownerTokenUTXO;
10975
11104
  try {
10976
- ownerTokenUTXO = await this.ownerTokenManager.findOwnerTokenUTXO(
10977
- ownerTokenName,
10978
- addresses
10979
- );
11105
+ ownerTokenUTXO = await ownerTokenLookup;
10980
11106
  } catch (error) {
10981
11107
  if (error instanceof OwnerTokenNotFoundError) {
10982
11108
  throw new OwnerTokenNotFoundError(
@@ -10992,11 +11118,18 @@ var NeuraiAssetsBundle = (function (exports) {
10992
11118
  const burnInfo = this.burnManager.getReissueBurn();
10993
11119
 
10994
11120
  // 8. Estimate fee
11121
+ // Las salidas de asset deben describirse como lo que el nodo serializa.
11122
+ // Como direcciones desnudas se contaban 34 bytes por salida y el payload
11123
+ // entero quedaba sin pagar; las de datos nulos (tag, congelación,
11124
+ // verificador) ni siquiera llevan destino: su script SUSTITUYE al P2PKH.
10995
11125
  const outputAddresses = [
10996
11126
  burnInfo.address,
10997
11127
  changeAddress,
10998
- changeAddress, // owner token return goes to change address
10999
- toAddress,
11128
+ ...(changeVerifier && newVerifier
11129
+ ? [{ kind: 'verifier', assetName, verifierString: newVerifier }]
11130
+ : []),
11131
+ { address: changeAddress, assetName: ownerTokenName },
11132
+ { address: toAddress, assetName, kind: 'reissue', hasIpfs: Boolean(this.params.ipfsHash) },
11000
11133
  ];
11001
11134
  // Fund the XNA side. The owner-token input counts towards the size
11002
11135
  // estimate from the first round and is excluded from XNA selection.
@@ -11256,7 +11389,20 @@ var NeuraiAssetsBundle = (function (exports) {
11256
11389
 
11257
11390
  // 6. Estimate fee
11258
11391
  // Outputs: burn + XNA change + tag/untag operation (sent to changeAddress)
11259
- const outputAddresses = [burnInfo.address, changeAddress, changeAddress];
11392
+ // Las salidas de asset deben describirse como lo que el nodo serializa.
11393
+ // Como direcciones desnudas se contaban 34 bytes por salida y el payload
11394
+ // entero quedaba sin pagar; las de datos nulos (tag, congelación,
11395
+ // verificador) ni siquiera llevan destino: su script SUSTITUYE al P2PKH.
11396
+ const outputAddresses = [
11397
+ burnInfo.address,
11398
+ changeAddress,
11399
+ // El nodo devuelve el resto del qualifier a la dirección de cambio.
11400
+ { address: changeAddress, assetName: qualifierName },
11401
+ // Una salida de datos nulos por dirección etiquetada.
11402
+ ...targetAddresses.map(target => ({
11403
+ address: target, assetName: qualifierName, kind: 'tag'
11404
+ }))
11405
+ ];
11260
11406
  // 7-11. Fund the XNA side. The qualifier inputs count towards the size
11261
11407
  // estimate from the first round and are excluded from XNA selection.
11262
11408
  const burnSats = this.xnaAmountToSats(burnInfo.amount, { label: 'burn amount' });
@@ -11470,6 +11616,17 @@ var NeuraiAssetsBundle = (function (exports) {
11470
11616
 
11471
11617
  const { assetName } = this.params;
11472
11618
 
11619
+ // El token owner no depende de nada de lo que sigue: pedirlo a la vez que
11620
+ // las lecturas del asset ahorra una ida y vuelta completa. Se ESPERA en su
11621
+ // sitio de siempre, así que el orden de los errores no cambia.
11622
+ const ownerTokenName = AssetNameParser.getOwnerTokenName(assetName);
11623
+ const addresses = await this._getAddresses();
11624
+ const ownerTokenLookup = this.ownerTokenManager.findOwnerTokenUTXO(
11625
+ ownerTokenName,
11626
+ addresses
11627
+ );
11628
+ ownerTokenLookup.catch(() => {});
11629
+
11473
11630
  // 2. Check if asset exists and is restricted
11474
11631
  const assetData = await this.getAssetData(assetName);
11475
11632
  if (!assetData) {
@@ -11480,17 +11637,12 @@ var NeuraiAssetsBundle = (function (exports) {
11480
11637
  }
11481
11638
 
11482
11639
  // 3. Get wallet addresses
11483
- const addresses = await this._getAddresses();
11484
11640
  const changeAddress = await this.getChangeAddress();
11485
11641
 
11486
11642
  // 4. Find owner token (CRITICAL: must have this)
11487
- const ownerTokenName = AssetNameParser.getOwnerTokenName(assetName);
11488
11643
  let ownerTokenUTXO;
11489
11644
  try {
11490
- ownerTokenUTXO = await this.ownerTokenManager.findOwnerTokenUTXO(
11491
- ownerTokenName,
11492
- addresses
11493
- );
11645
+ ownerTokenUTXO = await ownerTokenLookup;
11494
11646
  } catch (error) {
11495
11647
  if (error instanceof OwnerTokenNotFoundError) {
11496
11648
  throw new OwnerTokenNotFoundError(
@@ -11506,7 +11658,22 @@ var NeuraiAssetsBundle = (function (exports) {
11506
11658
 
11507
11659
  // 6. Estimate fee
11508
11660
  // Outputs: XNA change + freeze/unfreeze operation (sent to changeAddress)
11509
- const outputAddresses = [changeAddress, changeAddress];
11661
+ // Las salidas de asset deben describirse como lo que el nodo serializa.
11662
+ // Como direcciones desnudas se contaban 34 bytes por salida y el payload
11663
+ // entero quedaba sin pagar; las de datos nulos (tag, congelación,
11664
+ // verificador) ni siquiera llevan destino: su script SUSTITUYE al P2PKH.
11665
+ const isGlobal = operationType === 'FREEZE_ASSET' || operationType === 'UNFREEZE_ASSET';
11666
+ const frozenAddresses = isGlobal ? [] : (this.params.addresses || []);
11667
+ const outputAddresses = [
11668
+ changeAddress,
11669
+ // El token owner se gasta y se devuelve: transferencia.
11670
+ { address: changeAddress, assetName: ownerTokenName },
11671
+ ...(isGlobal
11672
+ ? [{ assetName, kind: 'globalRestriction' }]
11673
+ : frozenAddresses.map(target => ({
11674
+ address: target, assetName, kind: 'restriction'
11675
+ })))
11676
+ ];
11510
11677
  // 7-10. Fund the XNA side (fee only, this operation does not burn). The
11511
11678
  // owner-token input counts towards the size estimate from the first
11512
11679
  // round and is excluded from XNA selection.