@neuraiproject/neurai-assets 1.5.1 → 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.
- package/dist/NeuraiAssets.global.js +344 -125
- package/dist/NeuraiAssets.global.js.map +1 -1
- package/dist/browser.js +344 -125
- package/dist/browser.js.map +1 -1
- package/dist/index.cjs +344 -125
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +344 -125
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -5999,66 +5999,116 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
5999
5999
|
}
|
|
6000
6000
|
|
|
6001
6001
|
/**
|
|
6002
|
-
*
|
|
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:
|
|
6007
|
-
*
|
|
6008
|
-
* marker(3) + type(1) + nameLength(1) + name + kind-specific tail
|
|
6002
|
+
* Encoders that produce the exact scriptPubKey the node will see.
|
|
6009
6003
|
*
|
|
6010
|
-
* Sizing
|
|
6011
|
-
*
|
|
6012
|
-
*
|
|
6013
|
-
*
|
|
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.
|
|
6014
6010
|
*
|
|
6015
|
-
*
|
|
6016
|
-
*
|
|
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
|
-
|
|
6019
|
-
if (!descriptor || typeof descriptor !== 'object' || !descriptor.assetName) {
|
|
6020
|
-
return 0;
|
|
6021
|
-
}
|
|
6014
|
+
const ct = requireDist_1();
|
|
6022
6015
|
|
|
6023
|
-
|
|
6024
|
-
|
|
6025
|
-
|
|
6026
|
-
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
|
|
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
|
-
|
|
6032
|
-
|
|
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
|
-
|
|
6037
|
-
break;
|
|
6045
|
+
return ct.encodeOwnerAssetScript(address, assetName);
|
|
6038
6046
|
case 'issue':
|
|
6039
|
-
|
|
6040
|
-
payload += 11;
|
|
6041
|
-
break;
|
|
6047
|
+
return ct.encodeNewAssetScript(address, assetName, 0n, 0, true, ipfs);
|
|
6042
6048
|
case 'reissue':
|
|
6043
|
-
|
|
6044
|
-
|
|
6045
|
-
|
|
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
|
-
|
|
6048
|
-
payload += 8;
|
|
6049
|
-
break;
|
|
6061
|
+
return null;
|
|
6050
6062
|
}
|
|
6063
|
+
}
|
|
6051
6064
|
|
|
6052
|
-
|
|
6053
|
-
|
|
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
|
-
|
|
6057
|
-
|
|
6058
|
-
|
|
6059
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
@@ -6309,11 +6377,14 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
6309
6377
|
* @throws {InsufficientFundsError} If not enough funds
|
|
6310
6378
|
*/
|
|
6311
6379
|
async selectBaseCurrencyUTXOs(addresses, requiredAmount, buffer = 0.1, options = {}) {
|
|
6312
|
-
//
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
-
//
|
|
6316
|
-
const mempool = await
|
|
6380
|
+
// Both reads describe the same addresses and neither feeds the other: the
|
|
6381
|
+
// mempool result only filters the UTXO result afterwards. Awaiting them in
|
|
6382
|
+
// sequence spent one extra network round trip per selection, which on a
|
|
6383
|
+
// remote RPC proxy is most of the time a wallet spends building anything.
|
|
6384
|
+
const [allUTXOs, mempool] = await Promise.all([
|
|
6385
|
+
this.getUTXOs(addresses, null),
|
|
6386
|
+
this.getMempoolEntries(addresses)
|
|
6387
|
+
]);
|
|
6317
6388
|
const unspentUTXOs = this.filterMempoolSpentUTXOs(allUTXOs, mempool);
|
|
6318
6389
|
|
|
6319
6390
|
// Drop outpoints the caller already spends elsewhere in this transaction
|
|
@@ -6379,11 +6450,14 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
6379
6450
|
throw new Error('Asset name is required');
|
|
6380
6451
|
}
|
|
6381
6452
|
|
|
6382
|
-
//
|
|
6383
|
-
|
|
6384
|
-
|
|
6385
|
-
//
|
|
6386
|
-
const mempool = await
|
|
6453
|
+
// Both reads describe the same addresses and neither feeds the other: the
|
|
6454
|
+
// mempool result only filters the UTXO result afterwards. Awaiting them in
|
|
6455
|
+
// sequence spent one extra network round trip per selection, which on a
|
|
6456
|
+
// remote RPC proxy is most of the time a wallet spends building anything.
|
|
6457
|
+
const [allUTXOs, mempool] = await Promise.all([
|
|
6458
|
+
this.getUTXOs(addresses, assetName),
|
|
6459
|
+
this.getMempoolEntries(addresses)
|
|
6460
|
+
]);
|
|
6387
6461
|
const unspentUTXOs = this.filterMempoolSpentUTXOs(allUTXOs, mempool);
|
|
6388
6462
|
|
|
6389
6463
|
const excluded = toOutpointSet(options.exclude);
|
|
@@ -6505,8 +6579,10 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
6505
6579
|
* @returns {Promise<bigint>} Total balance in 10^8-scaled units
|
|
6506
6580
|
*/
|
|
6507
6581
|
async getBalanceRaw(addresses, assetName = null) {
|
|
6508
|
-
const utxos = await
|
|
6509
|
-
|
|
6582
|
+
const [utxos, mempool] = await Promise.all([
|
|
6583
|
+
this.getUTXOs(addresses, assetName),
|
|
6584
|
+
this.getMempoolEntries(addresses)
|
|
6585
|
+
]);
|
|
6510
6586
|
const availableUTXOs = this.filterMempoolSpentUTXOs(utxos, mempool);
|
|
6511
6587
|
|
|
6512
6588
|
return sumProtocolIntegers(availableUTXOs, 'satoshis', `${assetName || 'XNA'} utxo.satoshis`);
|
|
@@ -7994,6 +8070,44 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
7994
8070
|
throw new Error('build must be implemented by subclass');
|
|
7995
8071
|
}
|
|
7996
8072
|
|
|
8073
|
+
/**
|
|
8074
|
+
* Start the fee-rate lookup without waiting for it.
|
|
8075
|
+
*
|
|
8076
|
+
* Every build needs the fee rate, and it depends on nothing the build
|
|
8077
|
+
* computes, so there is no reason for it to wait its turn behind the reads
|
|
8078
|
+
* that come first. Kicking it off early lets it share a round trip with
|
|
8079
|
+
* them; `estimateFee`/`estimateFeeSats` await the same memoised promise, so
|
|
8080
|
+
* the call still happens exactly once and a failure still surfaces there.
|
|
8081
|
+
*
|
|
8082
|
+
* The trailing `catch` only marks the promise as handled while nothing is
|
|
8083
|
+
* awaiting it — it attaches to a derived promise, so the original still
|
|
8084
|
+
* rejects for whoever awaits it later.
|
|
8085
|
+
*
|
|
8086
|
+
* @returns {void}
|
|
8087
|
+
*/
|
|
8088
|
+
warmFeeRate() {
|
|
8089
|
+
if (this._feeRatePromise) {
|
|
8090
|
+
return;
|
|
8091
|
+
}
|
|
8092
|
+
this._feeRatePromise = this.utxoSelector.getFeeRate();
|
|
8093
|
+
this._feeRatePromise.catch(() => {});
|
|
8094
|
+
}
|
|
8095
|
+
|
|
8096
|
+
/**
|
|
8097
|
+
* Start every read a build needs but that depends on nothing the build
|
|
8098
|
+
* computes: the fee rate and the NIP-040 asset marker.
|
|
8099
|
+
*
|
|
8100
|
+
* Both are memoised, so warming them costs no extra call — it only moves
|
|
8101
|
+
* them off the critical path. Every builder that reaches here goes on to
|
|
8102
|
+
* stamp a marker, so neither read is ever speculative.
|
|
8103
|
+
*
|
|
8104
|
+
* @returns {void}
|
|
8105
|
+
*/
|
|
8106
|
+
warmChainReads() {
|
|
8107
|
+
this.warmFeeRate();
|
|
8108
|
+
this.resolveAssetMarker().catch(() => {});
|
|
8109
|
+
}
|
|
8110
|
+
|
|
7997
8111
|
/**
|
|
7998
8112
|
* Estimate transaction fee.
|
|
7999
8113
|
*
|
|
@@ -8007,9 +8121,7 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
8007
8121
|
* @returns {Promise<number>} Estimated fee in XNA
|
|
8008
8122
|
*/
|
|
8009
8123
|
async estimateFee(inputs, outputs) {
|
|
8010
|
-
|
|
8011
|
-
this._feeRatePromise = this.utxoSelector.getFeeRate();
|
|
8012
|
-
}
|
|
8124
|
+
this.warmFeeRate();
|
|
8013
8125
|
const feeRate = await this._feeRatePromise;
|
|
8014
8126
|
return this.utxoSelector.estimateFee(inputs, outputs, feeRate);
|
|
8015
8127
|
}
|
|
@@ -8040,9 +8152,7 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
8040
8152
|
* @returns {Promise<bigint>} Estimated fee in satoshis
|
|
8041
8153
|
*/
|
|
8042
8154
|
async estimateFeeSats(inputs, outputs) {
|
|
8043
|
-
|
|
8044
|
-
this._feeRatePromise = this.utxoSelector.getFeeRate();
|
|
8045
|
-
}
|
|
8155
|
+
this.warmFeeRate();
|
|
8046
8156
|
const feeRate = await this._feeRatePromise;
|
|
8047
8157
|
return this.utxoSelector.estimateFeeSats(inputs, outputs, feeRate);
|
|
8048
8158
|
}
|
|
@@ -8085,6 +8195,10 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
8085
8195
|
initialInputHint = 1
|
|
8086
8196
|
} = options;
|
|
8087
8197
|
|
|
8198
|
+
// Covers the builders that never call assetExists, whose first read is
|
|
8199
|
+
// this one.
|
|
8200
|
+
this.warmChainReads();
|
|
8201
|
+
|
|
8088
8202
|
const addresses = await this._getAddresses();
|
|
8089
8203
|
const excluded = UTXOSelector.toOutpointSet(exclude);
|
|
8090
8204
|
// toOutpointSet returns the caller's Set untouched when it already is one;
|
|
@@ -8714,6 +8828,9 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
8714
8828
|
* @returns {Promise<boolean>} True if exists
|
|
8715
8829
|
*/
|
|
8716
8830
|
async assetExists(assetName) {
|
|
8831
|
+
// This is the first read a build performs and its answer gates nothing but
|
|
8832
|
+
// the guard below, so let the build's other chain reads travel alongside it.
|
|
8833
|
+
this.warmChainReads();
|
|
8717
8834
|
try {
|
|
8718
8835
|
const assetData = await this.rpc('getassetdata', [assetName]);
|
|
8719
8836
|
return assetData !== null && assetData !== undefined;
|
|
@@ -8733,6 +8850,9 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
8733
8850
|
* @returns {Promise<object|null>} Asset data or null if not found
|
|
8734
8851
|
*/
|
|
8735
8852
|
async getAssetData(assetName) {
|
|
8853
|
+
// This is the first read a build performs and its answer gates nothing but
|
|
8854
|
+
// the guard below, so let the build's other chain reads travel alongside it.
|
|
8855
|
+
this.warmChainReads();
|
|
8736
8856
|
try {
|
|
8737
8857
|
return await this.rpc('getassetdata', [assetName]);
|
|
8738
8858
|
} catch (error) {
|
|
@@ -9031,8 +9151,24 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
9031
9151
|
throw new Error('Cannot parse parent asset from SUB asset name');
|
|
9032
9152
|
}
|
|
9033
9153
|
|
|
9034
|
-
//
|
|
9035
|
-
|
|
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
|
+
]);
|
|
9036
9172
|
if (!parentExists) {
|
|
9037
9173
|
throw new ParentAssetNotFoundError(
|
|
9038
9174
|
`Parent asset ${parentAssetName} does not exist. You must create the ROOT asset first.`,
|
|
@@ -9040,8 +9176,6 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
9040
9176
|
);
|
|
9041
9177
|
}
|
|
9042
9178
|
|
|
9043
|
-
// 4. Check if SUB asset already exists
|
|
9044
|
-
const subExists = await this.assetExists(assetName);
|
|
9045
9179
|
if (subExists) {
|
|
9046
9180
|
throw new AssetExistsError(
|
|
9047
9181
|
`Asset ${assetName} already exists on the blockchain`,
|
|
@@ -9050,18 +9184,13 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
9050
9184
|
}
|
|
9051
9185
|
|
|
9052
9186
|
// 5. Get addresses
|
|
9053
|
-
const addresses = await this._getAddresses();
|
|
9054
9187
|
const toAddress = await this.getToAddress();
|
|
9055
9188
|
const changeAddress = await this.getChangeAddress();
|
|
9056
9189
|
|
|
9057
9190
|
// 6. Find parent's owner token (CRITICAL: must have this)
|
|
9058
|
-
const ownerTokenName = AssetNameParser.getOwnerTokenName(parentAssetName);
|
|
9059
9191
|
let ownerTokenUTXO;
|
|
9060
9192
|
try {
|
|
9061
|
-
ownerTokenUTXO = await
|
|
9062
|
-
ownerTokenName,
|
|
9063
|
-
addresses
|
|
9064
|
-
);
|
|
9193
|
+
ownerTokenUTXO = await ownerTokenLookup;
|
|
9065
9194
|
} catch (error) {
|
|
9066
9195
|
if (error instanceof OwnerTokenNotFoundError) {
|
|
9067
9196
|
throw new OwnerTokenNotFoundError(
|
|
@@ -9082,7 +9211,11 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
9082
9211
|
const outputAddresses = [
|
|
9083
9212
|
burnInfo.address,
|
|
9084
9213
|
changeAddress,
|
|
9085
|
-
|
|
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 },
|
|
9086
9219
|
{ address: toAddress, assetName, kind: 'issue', hasIpfs },
|
|
9087
9220
|
{ address: changeAddress, assetName: `${assetName}!`, kind: 'owner' },
|
|
9088
9221
|
];
|
|
@@ -9466,6 +9599,19 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
9466
9599
|
newIpfs
|
|
9467
9600
|
} = this.params;
|
|
9468
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
|
+
|
|
9469
9615
|
// 2. Get asset data to verify it exists and is reissuable
|
|
9470
9616
|
const assetData = await this.getAssetData(assetName);
|
|
9471
9617
|
if (!assetData) {
|
|
@@ -9501,19 +9647,14 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
9501
9647
|
}
|
|
9502
9648
|
|
|
9503
9649
|
// 5. Get addresses
|
|
9504
|
-
const addresses = await this._getAddresses();
|
|
9505
9650
|
const toAddress = await this.getToAddress();
|
|
9506
9651
|
const changeAddress = await this.getChangeAddress();
|
|
9507
9652
|
const isDepinAsset = AssetNameParser.isDepin(assetName);
|
|
9508
9653
|
|
|
9509
9654
|
// 6. Find owner token (CRITICAL: must have this)
|
|
9510
|
-
const ownerTokenName = AssetNameParser.getOwnerTokenName(assetName);
|
|
9511
9655
|
let ownerTokenUTXO;
|
|
9512
9656
|
try {
|
|
9513
|
-
ownerTokenUTXO = await
|
|
9514
|
-
ownerTokenName,
|
|
9515
|
-
addresses
|
|
9516
|
-
);
|
|
9657
|
+
ownerTokenUTXO = await ownerTokenLookup;
|
|
9517
9658
|
} catch (error) {
|
|
9518
9659
|
if (error instanceof OwnerTokenNotFoundError) {
|
|
9519
9660
|
throw new OwnerTokenNotFoundError(
|
|
@@ -9534,7 +9675,11 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
9534
9675
|
const outputAddresses = [
|
|
9535
9676
|
burnInfo.address,
|
|
9536
9677
|
changeAddress,
|
|
9537
|
-
|
|
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 },
|
|
9538
9683
|
{ address: toAddress, assetName, kind: 'reissue', hasIpfs: Boolean(newIpfs) },
|
|
9539
9684
|
];
|
|
9540
9685
|
// Fund the XNA side. The owner-token input counts towards the size
|
|
@@ -9802,7 +9947,8 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
9802
9947
|
...recipients.map(r => ({ address: r.address, assetName })),
|
|
9803
9948
|
{ address: changeAddress, assetName }, // asset change (harmless over-count if absent)
|
|
9804
9949
|
...(isDepin
|
|
9805
|
-
|
|
9950
|
+
// Escolta: se gasta y se devuelve, luego es una transferencia.
|
|
9951
|
+
? [{ address: changeAddress, assetName: ownerTokenName }]
|
|
9806
9952
|
: []),
|
|
9807
9953
|
];
|
|
9808
9954
|
|
|
@@ -10086,6 +10232,17 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
10086
10232
|
ipfsHashes = []
|
|
10087
10233
|
} = this.params;
|
|
10088
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
|
+
|
|
10089
10246
|
// 2. Check if parent asset exists
|
|
10090
10247
|
const parentExists = await this.assetExists(rootName);
|
|
10091
10248
|
if (!parentExists) {
|
|
@@ -10095,31 +10252,31 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
10095
10252
|
);
|
|
10096
10253
|
}
|
|
10097
10254
|
|
|
10098
|
-
// 3. Check if any of the unique assets already exist
|
|
10099
|
-
|
|
10100
|
-
|
|
10101
|
-
|
|
10102
|
-
|
|
10103
|
-
|
|
10104
|
-
|
|
10105
|
-
|
|
10106
|
-
|
|
10107
|
-
|
|
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
|
+
);
|
|
10108
10270
|
}
|
|
10109
10271
|
|
|
10110
10272
|
// 4. Get addresses
|
|
10111
|
-
const addresses = await this._getAddresses();
|
|
10112
10273
|
const toAddress = await this.getToAddress();
|
|
10113
10274
|
const changeAddress = await this.getChangeAddress();
|
|
10114
10275
|
|
|
10115
10276
|
// 5. Find parent's owner token (CRITICAL: must have this)
|
|
10116
|
-
const ownerTokenName = AssetNameParser.getOwnerTokenName(rootName);
|
|
10117
10277
|
let ownerTokenUTXO;
|
|
10118
10278
|
try {
|
|
10119
|
-
ownerTokenUTXO = await
|
|
10120
|
-
ownerTokenName,
|
|
10121
|
-
addresses
|
|
10122
|
-
);
|
|
10279
|
+
ownerTokenUTXO = await ownerTokenLookup;
|
|
10123
10280
|
} catch (error) {
|
|
10124
10281
|
if (error instanceof OwnerTokenNotFoundError) {
|
|
10125
10282
|
throw new OwnerTokenNotFoundError(
|
|
@@ -10141,7 +10298,11 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
10141
10298
|
const outputAddresses = [
|
|
10142
10299
|
burnInfo.address,
|
|
10143
10300
|
changeAddress,
|
|
10144
|
-
|
|
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 },
|
|
10145
10306
|
...assetTags.map(tag => ({
|
|
10146
10307
|
address: toAddress,
|
|
10147
10308
|
assetName: `${rootName}#${tag}`,
|
|
@@ -10625,6 +10786,17 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
10625
10786
|
ipfsHash = ''
|
|
10626
10787
|
} = this.params;
|
|
10627
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
|
+
|
|
10628
10800
|
// 2. Check if asset already exists
|
|
10629
10801
|
const exists = await this.assetExists(assetName);
|
|
10630
10802
|
if (exists) {
|
|
@@ -10641,18 +10813,13 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
10641
10813
|
const burnInfo = this.burnManager.getIssueRestrictedBurn();
|
|
10642
10814
|
|
|
10643
10815
|
// 5. Get addresses
|
|
10644
|
-
const addresses = await this._getAddresses();
|
|
10645
10816
|
const toAddress = await this.getToAddress();
|
|
10646
10817
|
const changeAddress = await this.getChangeAddress();
|
|
10647
10818
|
|
|
10648
10819
|
// 6. Find owner token UTXO (CRITICAL: node requires it as input)
|
|
10649
|
-
const ownerTokenName = AssetNameParser.getOwnerTokenName(assetName);
|
|
10650
10820
|
let ownerTokenUTXO;
|
|
10651
10821
|
try {
|
|
10652
|
-
ownerTokenUTXO = await
|
|
10653
|
-
ownerTokenName,
|
|
10654
|
-
addresses
|
|
10655
|
-
);
|
|
10822
|
+
ownerTokenUTXO = await ownerTokenLookup;
|
|
10656
10823
|
} catch (error) {
|
|
10657
10824
|
if (error instanceof OwnerTokenNotFoundError) {
|
|
10658
10825
|
throw new OwnerTokenNotFoundError(
|
|
@@ -10664,11 +10831,16 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
10664
10831
|
}
|
|
10665
10832
|
|
|
10666
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.
|
|
10667
10838
|
const outputAddresses = [
|
|
10668
10839
|
burnInfo.address,
|
|
10669
10840
|
changeAddress,
|
|
10670
|
-
|
|
10671
|
-
|
|
10841
|
+
{ kind: 'verifier', assetName, verifierString },
|
|
10842
|
+
{ address: changeAddress, assetName: ownerTokenName },
|
|
10843
|
+
{ address: toAddress, assetName, kind: 'issue', hasIpfs: Boolean(this.params.ipfsHash) },
|
|
10672
10844
|
];
|
|
10673
10845
|
// Fund the XNA side. The owner-token input counts towards the size
|
|
10674
10846
|
// estimate from the first round and is excluded from XNA selection.
|
|
@@ -10878,6 +11050,17 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
10878
11050
|
newIpfs
|
|
10879
11051
|
} = this.params;
|
|
10880
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
|
+
|
|
10881
11064
|
// 2. Get asset data to verify it exists and is reissuable
|
|
10882
11065
|
const assetData = await this.getAssetData(assetName);
|
|
10883
11066
|
if (!assetData) {
|
|
@@ -10913,18 +11096,13 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
10913
11096
|
}
|
|
10914
11097
|
|
|
10915
11098
|
// 5. Get addresses
|
|
10916
|
-
const addresses = await this._getAddresses();
|
|
10917
11099
|
const toAddress = await this.getToAddress();
|
|
10918
11100
|
const changeAddress = await this.getChangeAddress();
|
|
10919
11101
|
|
|
10920
11102
|
// 6. Find owner token (CRITICAL: must have this)
|
|
10921
|
-
const ownerTokenName = AssetNameParser.getOwnerTokenName(assetName);
|
|
10922
11103
|
let ownerTokenUTXO;
|
|
10923
11104
|
try {
|
|
10924
|
-
ownerTokenUTXO = await
|
|
10925
|
-
ownerTokenName,
|
|
10926
|
-
addresses
|
|
10927
|
-
);
|
|
11105
|
+
ownerTokenUTXO = await ownerTokenLookup;
|
|
10928
11106
|
} catch (error) {
|
|
10929
11107
|
if (error instanceof OwnerTokenNotFoundError) {
|
|
10930
11108
|
throw new OwnerTokenNotFoundError(
|
|
@@ -10940,11 +11118,18 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
10940
11118
|
const burnInfo = this.burnManager.getReissueBurn();
|
|
10941
11119
|
|
|
10942
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.
|
|
10943
11125
|
const outputAddresses = [
|
|
10944
11126
|
burnInfo.address,
|
|
10945
11127
|
changeAddress,
|
|
10946
|
-
|
|
10947
|
-
|
|
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) },
|
|
10948
11133
|
];
|
|
10949
11134
|
// Fund the XNA side. The owner-token input counts towards the size
|
|
10950
11135
|
// estimate from the first round and is excluded from XNA selection.
|
|
@@ -11204,7 +11389,20 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
11204
11389
|
|
|
11205
11390
|
// 6. Estimate fee
|
|
11206
11391
|
// Outputs: burn + XNA change + tag/untag operation (sent to changeAddress)
|
|
11207
|
-
|
|
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
|
+
];
|
|
11208
11406
|
// 7-11. Fund the XNA side. The qualifier inputs count towards the size
|
|
11209
11407
|
// estimate from the first round and are excluded from XNA selection.
|
|
11210
11408
|
const burnSats = this.xnaAmountToSats(burnInfo.amount, { label: 'burn amount' });
|
|
@@ -11418,6 +11616,17 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
11418
11616
|
|
|
11419
11617
|
const { assetName } = this.params;
|
|
11420
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
|
+
|
|
11421
11630
|
// 2. Check if asset exists and is restricted
|
|
11422
11631
|
const assetData = await this.getAssetData(assetName);
|
|
11423
11632
|
if (!assetData) {
|
|
@@ -11428,17 +11637,12 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
11428
11637
|
}
|
|
11429
11638
|
|
|
11430
11639
|
// 3. Get wallet addresses
|
|
11431
|
-
const addresses = await this._getAddresses();
|
|
11432
11640
|
const changeAddress = await this.getChangeAddress();
|
|
11433
11641
|
|
|
11434
11642
|
// 4. Find owner token (CRITICAL: must have this)
|
|
11435
|
-
const ownerTokenName = AssetNameParser.getOwnerTokenName(assetName);
|
|
11436
11643
|
let ownerTokenUTXO;
|
|
11437
11644
|
try {
|
|
11438
|
-
ownerTokenUTXO = await
|
|
11439
|
-
ownerTokenName,
|
|
11440
|
-
addresses
|
|
11441
|
-
);
|
|
11645
|
+
ownerTokenUTXO = await ownerTokenLookup;
|
|
11442
11646
|
} catch (error) {
|
|
11443
11647
|
if (error instanceof OwnerTokenNotFoundError) {
|
|
11444
11648
|
throw new OwnerTokenNotFoundError(
|
|
@@ -11454,7 +11658,22 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
11454
11658
|
|
|
11455
11659
|
// 6. Estimate fee
|
|
11456
11660
|
// Outputs: XNA change + freeze/unfreeze operation (sent to changeAddress)
|
|
11457
|
-
|
|
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
|
+
];
|
|
11458
11677
|
// 7-10. Fund the XNA side (fee only, this operation does not burn). The
|
|
11459
11678
|
// owner-token input counts towards the size estimate from the first
|
|
11460
11679
|
// round and is excluded from XNA selection.
|