@neuraiproject/neurai-assets 1.2.3 → 1.3.0
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/README.md +45 -0
- package/dist/NeuraiAssets.global.js +473 -275
- package/dist/NeuraiAssets.global.js.map +1 -1
- package/dist/browser.js +473 -275
- package/dist/browser.js.map +1 -1
- package/dist/index.cjs +473 -275
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +473 -275
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
|
@@ -1091,45 +1091,45 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
1091
1091
|
};
|
|
1092
1092
|
|
|
1093
1093
|
/**
|
|
1094
|
-
* Asset naming
|
|
1094
|
+
* Asset naming helpers.
|
|
1095
|
+
* Network-specific maximum lengths are enforced in AssetNameValidator.
|
|
1095
1096
|
*/
|
|
1096
1097
|
const ASSET_NAME_RULES = {
|
|
1097
1098
|
ROOT: {
|
|
1098
1099
|
minLength: 3,
|
|
1099
|
-
maxLength:
|
|
1100
|
+
maxLength: 31,
|
|
1100
1101
|
pattern: /^[A-Z0-9_.]+$/,
|
|
1101
|
-
|
|
1102
|
-
reserved: ['XNA', 'NEURAI']
|
|
1102
|
+
reserved: ['XNA', 'NEURAI', 'NEURAICOIN']
|
|
1103
1103
|
},
|
|
1104
1104
|
SUB: {
|
|
1105
1105
|
minLength: 1,
|
|
1106
|
-
maxLength:
|
|
1106
|
+
maxLength: 31,
|
|
1107
1107
|
pattern: /^[A-Z0-9_.]+$/,
|
|
1108
1108
|
separator: '/',
|
|
1109
|
-
maxDepth:
|
|
1109
|
+
maxDepth: null
|
|
1110
1110
|
},
|
|
1111
1111
|
UNIQUE: {
|
|
1112
1112
|
minLength: 1,
|
|
1113
|
-
maxLength:
|
|
1114
|
-
pattern: /^[A-
|
|
1113
|
+
maxLength: 32,
|
|
1114
|
+
pattern: /^[-A-Za-z0-9@$%&*()[\]{}_.?:]+$/,
|
|
1115
1115
|
separator: '#'
|
|
1116
1116
|
},
|
|
1117
1117
|
QUALIFIER: {
|
|
1118
1118
|
minLength: 3,
|
|
1119
|
-
maxLength:
|
|
1120
|
-
pattern: /^[A-Z0-9_]+$/,
|
|
1119
|
+
maxLength: 32,
|
|
1120
|
+
pattern: /^[A-Z0-9_.]+$/,
|
|
1121
1121
|
prefix: '#',
|
|
1122
1122
|
separator: '/'
|
|
1123
1123
|
},
|
|
1124
1124
|
RESTRICTED: {
|
|
1125
1125
|
minLength: 3,
|
|
1126
|
-
maxLength:
|
|
1126
|
+
maxLength: 32,
|
|
1127
1127
|
pattern: /^[A-Z0-9_.]+$/,
|
|
1128
1128
|
prefix: '$'
|
|
1129
1129
|
},
|
|
1130
1130
|
DEPIN: {
|
|
1131
1131
|
minLength: 3,
|
|
1132
|
-
maxLength:
|
|
1132
|
+
maxLength: 121,
|
|
1133
1133
|
pattern: /^[A-Z0-9_.]+$/,
|
|
1134
1134
|
prefix: '&',
|
|
1135
1135
|
separator: '/'
|
|
@@ -2725,6 +2725,118 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
2725
2725
|
return OwnerTokenManager_1;
|
|
2726
2726
|
}
|
|
2727
2727
|
|
|
2728
|
+
/**
|
|
2729
|
+
* Fee / size helpers for Neurai transactions.
|
|
2730
|
+
*
|
|
2731
|
+
* These constants and classifiers are the same ones exposed by
|
|
2732
|
+
* `@neuraiproject/neurai-sign-transaction` (`VBYTES`, `isPQAddress`,
|
|
2733
|
+
* `isPQScript`, `estimateInputVbytes`, `estimateOutputBytes`,
|
|
2734
|
+
* `estimateTransactionVbytes`). They are inlined here to keep the assets
|
|
2735
|
+
* package light: depending on the full signer would pull `bitcoinjs-lib`
|
|
2736
|
+
* and `@noble/post-quantum` into the IIFE / browser bundles, which is far
|
|
2737
|
+
* more weight than the few constants we actually need for fee estimation.
|
|
2738
|
+
*
|
|
2739
|
+
* SOURCE OF TRUTH: `@neuraiproject/neurai-sign-transaction` `src/estimate.ts`.
|
|
2740
|
+
* Keep these values in sync with the signer's `VBYTES`. Mismatches surface
|
|
2741
|
+
* immediately as `min relay fee not met` failures from the node.
|
|
2742
|
+
*/
|
|
2743
|
+
|
|
2744
|
+
var feeSizing;
|
|
2745
|
+
var hasRequiredFeeSizing;
|
|
2746
|
+
|
|
2747
|
+
function requireFeeSizing () {
|
|
2748
|
+
if (hasRequiredFeeSizing) return feeSizing;
|
|
2749
|
+
hasRequiredFeeSizing = 1;
|
|
2750
|
+
/** Per-component byte sizes used across the Neurai stack for fee estimation. */
|
|
2751
|
+
const VBYTES = Object.freeze({
|
|
2752
|
+
/** Raw transaction overhead: version (4) + in-count varint (1) + out-count varint (1) + locktime (4). */
|
|
2753
|
+
baseTxOverheadBytes: 10,
|
|
2754
|
+
/** Extra weight contributed by the segwit marker + flag bytes when any input is PQ. */
|
|
2755
|
+
segwitMarkerVbytes: 1,
|
|
2756
|
+
/** vbytes for a typical legacy P2PKH input (worst-case scriptSig). */
|
|
2757
|
+
legacyInputVbytes: 148,
|
|
2758
|
+
/** vbytes for a typical PQ AuthScript input with the default OP_TRUE witnessScript. */
|
|
2759
|
+
pqInputVbytes: 977,
|
|
2760
|
+
/** Bytes of a legacy P2PKH output (8-byte value + 1-byte script length + 25-byte scriptPubKey). */
|
|
2761
|
+
legacyOutputBytes: 34,
|
|
2762
|
+
/** Bytes of an AuthScript-v1 output (8-byte value + 1-byte script length + 34-byte scriptPubKey). */
|
|
2763
|
+
pqOutputBytes: 43,
|
|
2764
|
+
});
|
|
2765
|
+
|
|
2766
|
+
/** True for Neurai PQ AuthScript bech32 destinations (`nq1…` mainnet, `tnq1…` testnet). */
|
|
2767
|
+
function isPQAddress(address) {
|
|
2768
|
+
return (
|
|
2769
|
+
typeof address === 'string' &&
|
|
2770
|
+
(address.startsWith('nq1') || address.startsWith('tnq1'))
|
|
2771
|
+
);
|
|
2772
|
+
}
|
|
2773
|
+
|
|
2774
|
+
/** True for AuthScript-v1 scriptPubKey hex (witness v1, 32-byte program — `5120…`). */
|
|
2775
|
+
function isPQScript(scriptHex) {
|
|
2776
|
+
if (typeof scriptHex !== 'string' || scriptHex.length < 4) return false;
|
|
2777
|
+
return scriptHex.toLowerCase().startsWith('5120');
|
|
2778
|
+
}
|
|
2779
|
+
|
|
2780
|
+
/**
|
|
2781
|
+
* Estimate the vbytes contributed by spending one UTXO. Uses the UTXO's
|
|
2782
|
+
* `script` if available, otherwise falls back to its `address`. Unknown
|
|
2783
|
+
* prevouts are treated as legacy.
|
|
2784
|
+
*/
|
|
2785
|
+
function estimateInputVbytes(utxo) {
|
|
2786
|
+
const script = utxo && utxo.script;
|
|
2787
|
+
if (typeof script === 'string' && script.length > 0) {
|
|
2788
|
+
return isPQScript(script) ? VBYTES.pqInputVbytes : VBYTES.legacyInputVbytes;
|
|
2789
|
+
}
|
|
2790
|
+
const address = utxo && utxo.address;
|
|
2791
|
+
if (typeof address === 'string' && isPQAddress(address)) {
|
|
2792
|
+
return VBYTES.pqInputVbytes;
|
|
2793
|
+
}
|
|
2794
|
+
return VBYTES.legacyInputVbytes;
|
|
2795
|
+
}
|
|
2796
|
+
|
|
2797
|
+
/** Estimate the bytes contributed by an output (address string or `{address}`). */
|
|
2798
|
+
function estimateOutputBytes(target) {
|
|
2799
|
+
const address =
|
|
2800
|
+
typeof target === 'string' ? target : (target && target.address) || '';
|
|
2801
|
+
return isPQAddress(address) ? VBYTES.pqOutputBytes : VBYTES.legacyOutputBytes;
|
|
2802
|
+
}
|
|
2803
|
+
|
|
2804
|
+
/**
|
|
2805
|
+
* Sum the per-input/per-output contributions, plus base overhead and segwit
|
|
2806
|
+
* marker (added once when any input is PQ). Inputs may be partial UTXO-like
|
|
2807
|
+
* objects with `script` and/or `address`. Outputs may be address strings or
|
|
2808
|
+
* `{ address }` descriptors.
|
|
2809
|
+
*/
|
|
2810
|
+
function estimateTransactionVbytes(inputs, outputs) {
|
|
2811
|
+
let vbytes = VBYTES.baseTxOverheadBytes;
|
|
2812
|
+
let hasPQInput = false;
|
|
2813
|
+
|
|
2814
|
+
for (const inp of inputs) {
|
|
2815
|
+
const v = estimateInputVbytes(inp);
|
|
2816
|
+
vbytes += v;
|
|
2817
|
+
if (v === VBYTES.pqInputVbytes) hasPQInput = true;
|
|
2818
|
+
}
|
|
2819
|
+
|
|
2820
|
+
for (const out of outputs) {
|
|
2821
|
+
vbytes += estimateOutputBytes(out);
|
|
2822
|
+
}
|
|
2823
|
+
|
|
2824
|
+
if (hasPQInput) vbytes += VBYTES.segwitMarkerVbytes;
|
|
2825
|
+
|
|
2826
|
+
return vbytes;
|
|
2827
|
+
}
|
|
2828
|
+
|
|
2829
|
+
feeSizing = {
|
|
2830
|
+
VBYTES,
|
|
2831
|
+
isPQAddress,
|
|
2832
|
+
isPQScript,
|
|
2833
|
+
estimateInputVbytes,
|
|
2834
|
+
estimateOutputBytes,
|
|
2835
|
+
estimateTransactionVbytes,
|
|
2836
|
+
};
|
|
2837
|
+
return feeSizing;
|
|
2838
|
+
}
|
|
2839
|
+
|
|
2728
2840
|
/**
|
|
2729
2841
|
* UTXO Selector
|
|
2730
2842
|
* Selects appropriate UTXOs for asset transactions
|
|
@@ -2742,6 +2854,7 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
2742
2854
|
if (hasRequiredUTXOSelector) return UTXOSelector_1;
|
|
2743
2855
|
hasRequiredUTXOSelector = 1;
|
|
2744
2856
|
const { InsufficientFundsError } = requireErrors();
|
|
2857
|
+
const { estimateTransactionVbytes } = requireFeeSizing();
|
|
2745
2858
|
|
|
2746
2859
|
class UTXOSelector {
|
|
2747
2860
|
/**
|
|
@@ -2980,35 +3093,44 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
2980
3093
|
}
|
|
2981
3094
|
|
|
2982
3095
|
/**
|
|
2983
|
-
* Estimate transaction size in
|
|
2984
|
-
*
|
|
3096
|
+
* Estimate transaction size in vbytes for fee calculation.
|
|
3097
|
+
*
|
|
3098
|
+
* Both arguments accept either a count (legacy callers) or an array of
|
|
3099
|
+
* descriptors that allow the estimator to distinguish PQ AuthScript
|
|
3100
|
+
* inputs/outputs from legacy P2PKH ones — PQ inputs are roughly six
|
|
3101
|
+
* times larger than legacy inputs and would otherwise underflow the
|
|
3102
|
+
* node's `min relay fee`.
|
|
2985
3103
|
*
|
|
2986
|
-
*
|
|
2987
|
-
*
|
|
2988
|
-
*
|
|
3104
|
+
* Input descriptors may be UTXO-like objects with `script` and/or
|
|
3105
|
+
* `address`. Output descriptors may be address strings or `{ address }`.
|
|
3106
|
+
* When a count is provided instead of an array, every input/output is
|
|
3107
|
+
* treated as legacy.
|
|
3108
|
+
*
|
|
3109
|
+
* @param {number|Array} inputs - Input count or array of UTXO-like descriptors
|
|
3110
|
+
* @param {number|Array} outputs - Output count or array of address-like descriptors
|
|
3111
|
+
* @returns {number} Estimated vbytes
|
|
2989
3112
|
*/
|
|
2990
|
-
estimateTransactionSize(
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
return inputSize + outputSize + overhead;
|
|
3113
|
+
estimateTransactionSize(inputs, outputs) {
|
|
3114
|
+
const inputDescriptors = Array.isArray(inputs)
|
|
3115
|
+
? inputs
|
|
3116
|
+
: new Array(inputs).fill({});
|
|
3117
|
+
const outputDescriptors = Array.isArray(outputs)
|
|
3118
|
+
? outputs
|
|
3119
|
+
: new Array(outputs).fill({});
|
|
3120
|
+
return estimateTransactionVbytes(inputDescriptors, outputDescriptors);
|
|
3000
3121
|
}
|
|
3001
3122
|
|
|
3002
3123
|
/**
|
|
3003
|
-
* Estimate fee for a transaction
|
|
3004
|
-
*
|
|
3005
|
-
* @param {number}
|
|
3124
|
+
* Estimate fee for a transaction.
|
|
3125
|
+
*
|
|
3126
|
+
* @param {number|Array} inputs - Input count or array of UTXO-like descriptors
|
|
3127
|
+
* @param {number|Array} outputs - Output count or array of address-like descriptors
|
|
3006
3128
|
* @param {number} feeRate - Fee rate in XNA per KB (default: 0.015)
|
|
3007
3129
|
* @returns {number} Estimated fee in XNA
|
|
3008
3130
|
*/
|
|
3009
|
-
estimateFee(
|
|
3010
|
-
const
|
|
3011
|
-
const sizeKB =
|
|
3131
|
+
estimateFee(inputs, outputs, feeRate = 0.015) {
|
|
3132
|
+
const sizeVbytes = this.estimateTransactionSize(inputs, outputs);
|
|
3133
|
+
const sizeKB = sizeVbytes / 1000;
|
|
3012
3134
|
const fee = sizeKB * feeRate;
|
|
3013
3135
|
|
|
3014
3136
|
// Round up to 8 decimals
|
|
@@ -3370,52 +3492,164 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
3370
3492
|
const { ASSET_NAME_RULES } = requireConstants();
|
|
3371
3493
|
const { InvalidAssetNameError } = requireErrors();
|
|
3372
3494
|
|
|
3495
|
+
const MIN_ASSET_LENGTH = 3;
|
|
3496
|
+
const MAINNET_MAX_NAME_LENGTH = 32;
|
|
3497
|
+
const TESTNET_MAX_NAME_LENGTH = 121;
|
|
3498
|
+
|
|
3499
|
+
const ROOT_NAME_CHARACTERS = /^[A-Z0-9._]{3,}$/;
|
|
3500
|
+
const SUB_NAME_CHARACTERS = /^[A-Z0-9._]+$/;
|
|
3501
|
+
const UNIQUE_TAG_CHARACTERS = /^[-A-Za-z0-9@$%&*()[\]{}_.?:]+$/;
|
|
3502
|
+
const QUALIFIER_NAME_CHARACTERS = /^#[A-Z0-9._]{3,}$/;
|
|
3503
|
+
const SUB_QUALIFIER_NAME_CHARACTERS = /^#[A-Z0-9._]+$/;
|
|
3504
|
+
const RESTRICTED_NAME_CHARACTERS = /^\$[A-Z0-9._]{3,}$/;
|
|
3505
|
+
const DEPIN_NAME_CHARACTERS = /^&[A-Z0-9._]{3,}$/;
|
|
3506
|
+
const SUB_DEPIN_NAME_CHARACTERS = /^&[A-Z0-9._]+\/[A-Z0-9._/]+$/;
|
|
3507
|
+
const DOUBLE_PUNCTUATION = /^.*[._]{2,}.*$/;
|
|
3508
|
+
const LEADING_PUNCTUATION = /^[._].*$/;
|
|
3509
|
+
const TRAILING_PUNCTUATION = /^.*[._]$/;
|
|
3510
|
+
const QUALIFIER_LEADING_PUNCTUATION = /^[#$][._].*$/;
|
|
3511
|
+
const NEURAI_NAMES = /^XNA$|^NEURAI$|^NEURAICOIN$|^#XNA$|^#NEURAI$|^#NEURAICOIN$/;
|
|
3512
|
+
|
|
3513
|
+
function isTestnet(network) {
|
|
3514
|
+
return typeof network === 'string' && network.toLowerCase().includes('test');
|
|
3515
|
+
}
|
|
3516
|
+
|
|
3517
|
+
function getMaxAssetNameLength(network) {
|
|
3518
|
+
return isTestnet(network) ? TESTNET_MAX_NAME_LENGTH : MAINNET_MAX_NAME_LENGTH;
|
|
3519
|
+
}
|
|
3520
|
+
|
|
3521
|
+
function getRootOrSubMaxLength(network) {
|
|
3522
|
+
return getMaxAssetNameLength(network) - 1;
|
|
3523
|
+
}
|
|
3524
|
+
|
|
3525
|
+
function ensureName(name, label) {
|
|
3526
|
+
if (!name || typeof name !== 'string') {
|
|
3527
|
+
throw new InvalidAssetNameError(`${label} must be a non-empty string`, name);
|
|
3528
|
+
}
|
|
3529
|
+
}
|
|
3530
|
+
|
|
3531
|
+
function ensureMaxLength(name, maxLength, label) {
|
|
3532
|
+
if (name.length > maxLength) {
|
|
3533
|
+
throw new InvalidAssetNameError(`${label} cannot exceed ${maxLength} characters`, name);
|
|
3534
|
+
}
|
|
3535
|
+
}
|
|
3536
|
+
|
|
3537
|
+
function ensureUppercase(name, message) {
|
|
3538
|
+
if (name !== name.toUpperCase()) {
|
|
3539
|
+
throw new InvalidAssetNameError(message, name);
|
|
3540
|
+
}
|
|
3541
|
+
}
|
|
3542
|
+
|
|
3543
|
+
function isRootNameValid(name) {
|
|
3544
|
+
return ROOT_NAME_CHARACTERS.test(name)
|
|
3545
|
+
&& !DOUBLE_PUNCTUATION.test(name)
|
|
3546
|
+
&& !LEADING_PUNCTUATION.test(name)
|
|
3547
|
+
&& !TRAILING_PUNCTUATION.test(name)
|
|
3548
|
+
&& !NEURAI_NAMES.test(name);
|
|
3549
|
+
}
|
|
3550
|
+
|
|
3551
|
+
function isSubNameValid(name) {
|
|
3552
|
+
return SUB_NAME_CHARACTERS.test(name)
|
|
3553
|
+
&& !DOUBLE_PUNCTUATION.test(name)
|
|
3554
|
+
&& !LEADING_PUNCTUATION.test(name)
|
|
3555
|
+
&& !TRAILING_PUNCTUATION.test(name);
|
|
3556
|
+
}
|
|
3557
|
+
|
|
3558
|
+
function isQualifierNameValid(name) {
|
|
3559
|
+
return QUALIFIER_NAME_CHARACTERS.test(name)
|
|
3560
|
+
&& !DOUBLE_PUNCTUATION.test(name)
|
|
3561
|
+
&& !QUALIFIER_LEADING_PUNCTUATION.test(name)
|
|
3562
|
+
&& !TRAILING_PUNCTUATION.test(name)
|
|
3563
|
+
&& !NEURAI_NAMES.test(name);
|
|
3564
|
+
}
|
|
3565
|
+
|
|
3566
|
+
function isSubQualifierNameValid(name) {
|
|
3567
|
+
return SUB_QUALIFIER_NAME_CHARACTERS.test(name)
|
|
3568
|
+
&& !DOUBLE_PUNCTUATION.test(name)
|
|
3569
|
+
&& !LEADING_PUNCTUATION.test(name)
|
|
3570
|
+
&& !TRAILING_PUNCTUATION.test(name);
|
|
3571
|
+
}
|
|
3572
|
+
|
|
3573
|
+
function isRestrictedNameValid(name) {
|
|
3574
|
+
return RESTRICTED_NAME_CHARACTERS.test(name)
|
|
3575
|
+
&& !DOUBLE_PUNCTUATION.test(name)
|
|
3576
|
+
&& !LEADING_PUNCTUATION.test(name)
|
|
3577
|
+
&& !TRAILING_PUNCTUATION.test(name)
|
|
3578
|
+
&& !NEURAI_NAMES.test(name);
|
|
3579
|
+
}
|
|
3580
|
+
|
|
3581
|
+
function isNameValidBeforeTag(name) {
|
|
3582
|
+
const parts = name.split('/');
|
|
3583
|
+
|
|
3584
|
+
if (!isRootNameValid(parts[0])) {
|
|
3585
|
+
return false;
|
|
3586
|
+
}
|
|
3587
|
+
|
|
3588
|
+
for (let index = 1; index < parts.length; index += 1) {
|
|
3589
|
+
if (!isSubNameValid(parts[index])) {
|
|
3590
|
+
return false;
|
|
3591
|
+
}
|
|
3592
|
+
}
|
|
3593
|
+
|
|
3594
|
+
return true;
|
|
3595
|
+
}
|
|
3596
|
+
|
|
3597
|
+
function isQualifierNameValidBeforeTag(name) {
|
|
3598
|
+
const parts = name.split('/');
|
|
3599
|
+
|
|
3600
|
+
if (!isQualifierNameValid(parts[0])) {
|
|
3601
|
+
return false;
|
|
3602
|
+
}
|
|
3603
|
+
|
|
3604
|
+
if (parts.length > 2) {
|
|
3605
|
+
return false;
|
|
3606
|
+
}
|
|
3607
|
+
|
|
3608
|
+
for (let index = 1; index < parts.length; index += 1) {
|
|
3609
|
+
if (!isSubQualifierNameValid(parts[index])) {
|
|
3610
|
+
return false;
|
|
3611
|
+
}
|
|
3612
|
+
}
|
|
3613
|
+
|
|
3614
|
+
return true;
|
|
3615
|
+
}
|
|
3616
|
+
|
|
3617
|
+
function isDepinIndicator(name) {
|
|
3618
|
+
return DEPIN_NAME_CHARACTERS.test(name) || SUB_DEPIN_NAME_CHARACTERS.test(name);
|
|
3619
|
+
}
|
|
3620
|
+
|
|
3373
3621
|
class AssetNameValidator {
|
|
3374
3622
|
/**
|
|
3375
3623
|
* Validate ROOT asset name
|
|
3376
|
-
* Rules: 3-
|
|
3377
|
-
* Cannot start with period,
|
|
3624
|
+
* Rules: 3-31 visible characters on mainnet, A-Z, 0-9, underscore, period
|
|
3625
|
+
* Cannot start or end with period/underscore, cannot use repeated punctuation
|
|
3378
3626
|
* Cannot be reserved names
|
|
3379
3627
|
*/
|
|
3380
|
-
static validateRoot(name) {
|
|
3381
|
-
|
|
3382
|
-
throw new InvalidAssetNameError('Asset name must be a non-empty string', name);
|
|
3383
|
-
}
|
|
3628
|
+
static validateRoot(name, network = 'xna') {
|
|
3629
|
+
ensureName(name, 'Asset name');
|
|
3384
3630
|
|
|
3385
|
-
|
|
3386
|
-
|
|
3631
|
+
const maxLength = getRootOrSubMaxLength(network);
|
|
3632
|
+
|
|
3633
|
+
if (name.length < MIN_ASSET_LENGTH || name.length > maxLength) {
|
|
3387
3634
|
throw new InvalidAssetNameError(
|
|
3388
|
-
`ROOT asset name must be ${
|
|
3635
|
+
`ROOT asset name must be ${MIN_ASSET_LENGTH}-${maxLength} characters`,
|
|
3389
3636
|
name
|
|
3390
3637
|
);
|
|
3391
3638
|
}
|
|
3392
3639
|
|
|
3393
|
-
|
|
3394
|
-
if (name !== name.toUpperCase()) {
|
|
3395
|
-
throw new InvalidAssetNameError('Asset name must be uppercase', name);
|
|
3396
|
-
}
|
|
3640
|
+
ensureUppercase(name, 'Asset name must be uppercase');
|
|
3397
3641
|
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
throw new InvalidAssetNameError(
|
|
3401
|
-
`Asset name cannot start with: ${ASSET_NAME_RULES.ROOT.cannotStartWith.join(', ')}`,
|
|
3402
|
-
name
|
|
3403
|
-
);
|
|
3642
|
+
if (NEURAI_NAMES.test(name)) {
|
|
3643
|
+
throw new InvalidAssetNameError(`${name} is a reserved asset name`, name);
|
|
3404
3644
|
}
|
|
3405
3645
|
|
|
3406
|
-
|
|
3407
|
-
if (!ASSET_NAME_RULES.ROOT.pattern.test(name)) {
|
|
3646
|
+
if (!isRootNameValid(name)) {
|
|
3408
3647
|
throw new InvalidAssetNameError(
|
|
3409
|
-
'
|
|
3648
|
+
'Name contains invalid characters (Valid characters are: A-Z 0-9 _ .) (special characters can\'t be the first or last characters)',
|
|
3410
3649
|
name
|
|
3411
3650
|
);
|
|
3412
3651
|
}
|
|
3413
3652
|
|
|
3414
|
-
// Reserved names check
|
|
3415
|
-
if (ASSET_NAME_RULES.ROOT.reserved.includes(name)) {
|
|
3416
|
-
throw new InvalidAssetNameError(`${name} is a reserved asset name`, name);
|
|
3417
|
-
}
|
|
3418
|
-
|
|
3419
3653
|
return true;
|
|
3420
3654
|
}
|
|
3421
3655
|
|
|
@@ -3423,39 +3657,25 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
3423
3657
|
* Validate SUB asset name
|
|
3424
3658
|
* Format: ROOT/SUBNAME
|
|
3425
3659
|
*/
|
|
3426
|
-
static validateSub(name) {
|
|
3427
|
-
|
|
3428
|
-
throw new InvalidAssetNameError('SUB asset name must be a non-empty string', name);
|
|
3429
|
-
}
|
|
3660
|
+
static validateSub(name, network = 'xna') {
|
|
3661
|
+
ensureName(name, 'SUB asset name');
|
|
3430
3662
|
|
|
3431
|
-
|
|
3432
|
-
if (parts.length !== 2) {
|
|
3663
|
+
if (!name.includes(ASSET_NAME_RULES.SUB.separator)) {
|
|
3433
3664
|
throw new InvalidAssetNameError(
|
|
3434
3665
|
`SUB asset must be in ${ASSET_NAME_RULES.SUB.separator} format (ROOT/SUBNAME)`,
|
|
3435
3666
|
name
|
|
3436
3667
|
);
|
|
3437
3668
|
}
|
|
3438
3669
|
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
// Validate root part
|
|
3442
|
-
this.validateRoot(rootName);
|
|
3443
|
-
|
|
3444
|
-
// Validate sub part
|
|
3445
|
-
if (subName.length < ASSET_NAME_RULES.SUB.minLength || subName.length > ASSET_NAME_RULES.SUB.maxLength) {
|
|
3446
|
-
throw new InvalidAssetNameError(
|
|
3447
|
-
`SUB asset name must be ${ASSET_NAME_RULES.SUB.minLength}-${ASSET_NAME_RULES.SUB.maxLength} characters`,
|
|
3448
|
-
name
|
|
3449
|
-
);
|
|
3450
|
-
}
|
|
3670
|
+
ensureMaxLength(name, getRootOrSubMaxLength(network), 'SUB asset name');
|
|
3451
3671
|
|
|
3452
|
-
if (
|
|
3672
|
+
if (name !== name.toUpperCase()) {
|
|
3453
3673
|
throw new InvalidAssetNameError('SUB asset name must be uppercase', name);
|
|
3454
3674
|
}
|
|
3455
3675
|
|
|
3456
|
-
if (!
|
|
3676
|
+
if (!isNameValidBeforeTag(name)) {
|
|
3457
3677
|
throw new InvalidAssetNameError(
|
|
3458
|
-
'
|
|
3678
|
+
'Name contains invalid characters (Valid characters are: A-Z 0-9 _ .) (special characters can\'t be the first or last characters)',
|
|
3459
3679
|
name
|
|
3460
3680
|
);
|
|
3461
3681
|
}
|
|
@@ -3467,10 +3687,10 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
3467
3687
|
* Validate UNIQUE asset name
|
|
3468
3688
|
* Format: ROOT#TAG
|
|
3469
3689
|
*/
|
|
3470
|
-
static validateUnique(name) {
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3690
|
+
static validateUnique(name, network = 'xna') {
|
|
3691
|
+
ensureName(name, 'UNIQUE asset name');
|
|
3692
|
+
|
|
3693
|
+
ensureMaxLength(name, getMaxAssetNameLength(network), 'UNIQUE asset name');
|
|
3474
3694
|
|
|
3475
3695
|
const parts = name.split(ASSET_NAME_RULES.UNIQUE.separator);
|
|
3476
3696
|
if (parts.length !== 2) {
|
|
@@ -3482,24 +3702,9 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
3482
3702
|
|
|
3483
3703
|
const [rootName, tag] = parts;
|
|
3484
3704
|
|
|
3485
|
-
|
|
3486
|
-
this.validateRoot(rootName);
|
|
3487
|
-
|
|
3488
|
-
// Validate tag
|
|
3489
|
-
if (tag.length < ASSET_NAME_RULES.UNIQUE.minLength || tag.length > ASSET_NAME_RULES.UNIQUE.maxLength) {
|
|
3490
|
-
throw new InvalidAssetNameError(
|
|
3491
|
-
`UNIQUE tag must be ${ASSET_NAME_RULES.UNIQUE.minLength}-${ASSET_NAME_RULES.UNIQUE.maxLength} characters`,
|
|
3492
|
-
name
|
|
3493
|
-
);
|
|
3494
|
-
}
|
|
3495
|
-
|
|
3496
|
-
if (tag !== tag.toUpperCase()) {
|
|
3497
|
-
throw new InvalidAssetNameError('UNIQUE tag must be uppercase', name);
|
|
3498
|
-
}
|
|
3499
|
-
|
|
3500
|
-
if (!ASSET_NAME_RULES.UNIQUE.pattern.test(tag)) {
|
|
3705
|
+
if (!isNameValidBeforeTag(rootName) || !UNIQUE_TAG_CHARACTERS.test(tag)) {
|
|
3501
3706
|
throw new InvalidAssetNameError(
|
|
3502
|
-
'
|
|
3707
|
+
'Unique name contains invalid characters (Valid characters are: A-Z a-z 0-9 @ $ % & * ( ) [ ] { } _ . ? : -)',
|
|
3503
3708
|
name
|
|
3504
3709
|
);
|
|
3505
3710
|
}
|
|
@@ -3511,10 +3716,8 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
3511
3716
|
* Validate QUALIFIER asset name
|
|
3512
3717
|
* Format: #NAME or #ROOT/SUB
|
|
3513
3718
|
*/
|
|
3514
|
-
static validateQualifier(name) {
|
|
3515
|
-
|
|
3516
|
-
throw new InvalidAssetNameError('QUALIFIER asset name must be a non-empty string', name);
|
|
3517
|
-
}
|
|
3719
|
+
static validateQualifier(name, network = 'xna') {
|
|
3720
|
+
ensureName(name, 'QUALIFIER asset name');
|
|
3518
3721
|
|
|
3519
3722
|
if (!name.startsWith(ASSET_NAME_RULES.QUALIFIER.prefix)) {
|
|
3520
3723
|
throw new InvalidAssetNameError(
|
|
@@ -3523,58 +3726,17 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
3523
3726
|
);
|
|
3524
3727
|
}
|
|
3525
3728
|
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
if (withoutPrefix.includes(ASSET_NAME_RULES.QUALIFIER.separator)) {
|
|
3529
|
-
// Sub-qualifier: #ROOT/SUB
|
|
3530
|
-
const parts = withoutPrefix.split(ASSET_NAME_RULES.QUALIFIER.separator);
|
|
3531
|
-
if (parts.length !== 2) {
|
|
3532
|
-
throw new InvalidAssetNameError(
|
|
3533
|
-
'SUB_QUALIFIER must be in #ROOT/SUB format',
|
|
3534
|
-
name
|
|
3535
|
-
);
|
|
3536
|
-
}
|
|
3537
|
-
|
|
3538
|
-
// Validate each part as a qualifier name (without the #)
|
|
3539
|
-
parts.forEach(part => {
|
|
3540
|
-
if (part.length < ASSET_NAME_RULES.QUALIFIER.minLength || part.length > ASSET_NAME_RULES.QUALIFIER.maxLength) {
|
|
3541
|
-
throw new InvalidAssetNameError(
|
|
3542
|
-
`QUALIFIER name must be ${ASSET_NAME_RULES.QUALIFIER.minLength}-${ASSET_NAME_RULES.QUALIFIER.maxLength} characters`,
|
|
3543
|
-
name
|
|
3544
|
-
);
|
|
3545
|
-
}
|
|
3729
|
+
ensureMaxLength(name, getMaxAssetNameLength(network), 'QUALIFIER asset name');
|
|
3546
3730
|
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
if (!ASSET_NAME_RULES.QUALIFIER.pattern.test(part)) {
|
|
3552
|
-
throw new InvalidAssetNameError(
|
|
3553
|
-
'QUALIFIER name can only contain A-Z, 0-9, and underscore',
|
|
3554
|
-
name
|
|
3555
|
-
);
|
|
3556
|
-
}
|
|
3557
|
-
});
|
|
3558
|
-
} else {
|
|
3559
|
-
// Root qualifier: #NAME
|
|
3560
|
-
if (withoutPrefix.length < ASSET_NAME_RULES.QUALIFIER.minLength ||
|
|
3561
|
-
withoutPrefix.length > ASSET_NAME_RULES.QUALIFIER.maxLength) {
|
|
3562
|
-
throw new InvalidAssetNameError(
|
|
3563
|
-
`QUALIFIER name must be ${ASSET_NAME_RULES.QUALIFIER.minLength}-${ASSET_NAME_RULES.QUALIFIER.maxLength} characters`,
|
|
3564
|
-
name
|
|
3565
|
-
);
|
|
3566
|
-
}
|
|
3567
|
-
|
|
3568
|
-
if (withoutPrefix !== withoutPrefix.toUpperCase()) {
|
|
3569
|
-
throw new InvalidAssetNameError('QUALIFIER name must be uppercase', name);
|
|
3570
|
-
}
|
|
3731
|
+
if (name !== name.toUpperCase()) {
|
|
3732
|
+
throw new InvalidAssetNameError('QUALIFIER name must be uppercase', name);
|
|
3733
|
+
}
|
|
3571
3734
|
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
}
|
|
3735
|
+
if (!isQualifierNameValidBeforeTag(name)) {
|
|
3736
|
+
throw new InvalidAssetNameError(
|
|
3737
|
+
'Qualifier name contains invalid characters (Valid characters are: A-Z 0-9 _ .) (# must be the first character, _ . special characters can\'t be the first or last characters)',
|
|
3738
|
+
name
|
|
3739
|
+
);
|
|
3578
3740
|
}
|
|
3579
3741
|
|
|
3580
3742
|
return true;
|
|
@@ -3584,10 +3746,8 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
3584
3746
|
* Validate RESTRICTED asset name
|
|
3585
3747
|
* Format: $NAME
|
|
3586
3748
|
*/
|
|
3587
|
-
static validateRestricted(name) {
|
|
3588
|
-
|
|
3589
|
-
throw new InvalidAssetNameError('RESTRICTED asset name must be a non-empty string', name);
|
|
3590
|
-
}
|
|
3749
|
+
static validateRestricted(name, network = 'xna') {
|
|
3750
|
+
ensureName(name, 'RESTRICTED asset name');
|
|
3591
3751
|
|
|
3592
3752
|
if (!name.startsWith(ASSET_NAME_RULES.RESTRICTED.prefix)) {
|
|
3593
3753
|
throw new InvalidAssetNameError(
|
|
@@ -3596,10 +3756,18 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
3596
3756
|
);
|
|
3597
3757
|
}
|
|
3598
3758
|
|
|
3599
|
-
|
|
3759
|
+
ensureMaxLength(name, getMaxAssetNameLength(network), 'RESTRICTED asset name');
|
|
3760
|
+
|
|
3761
|
+
if (name !== name.toUpperCase()) {
|
|
3762
|
+
throw new InvalidAssetNameError('RESTRICTED asset name must be uppercase', name);
|
|
3763
|
+
}
|
|
3600
3764
|
|
|
3601
|
-
|
|
3602
|
-
|
|
3765
|
+
if (!isRestrictedNameValid(name)) {
|
|
3766
|
+
throw new InvalidAssetNameError(
|
|
3767
|
+
'Restricted name contains invalid characters (Valid characters are: A-Z 0-9 _ .) ($ must be the first character, _ . special characters can\'t be the first or last characters)',
|
|
3768
|
+
name
|
|
3769
|
+
);
|
|
3770
|
+
}
|
|
3603
3771
|
|
|
3604
3772
|
return true;
|
|
3605
3773
|
}
|
|
@@ -3608,10 +3776,8 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
3608
3776
|
* Validate DEPIN asset name
|
|
3609
3777
|
* Format: &NAME or &NAME/SUB[/...]
|
|
3610
3778
|
*/
|
|
3611
|
-
static validateDepin(name) {
|
|
3612
|
-
|
|
3613
|
-
throw new InvalidAssetNameError('DEPIN asset name must be a non-empty string', name);
|
|
3614
|
-
}
|
|
3779
|
+
static validateDepin(name, network) {
|
|
3780
|
+
ensureName(name, 'DEPIN asset name');
|
|
3615
3781
|
|
|
3616
3782
|
if (!name.startsWith(ASSET_NAME_RULES.DEPIN.prefix)) {
|
|
3617
3783
|
throw new InvalidAssetNameError(
|
|
@@ -3620,58 +3786,41 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
3620
3786
|
);
|
|
3621
3787
|
}
|
|
3622
3788
|
|
|
3623
|
-
if (
|
|
3624
|
-
throw new InvalidAssetNameError(
|
|
3625
|
-
`DEPIN asset name cannot exceed ${ASSET_NAME_RULES.DEPIN.maxLength} characters`,
|
|
3626
|
-
name
|
|
3627
|
-
);
|
|
3789
|
+
if (network && !isTestnet(network)) {
|
|
3790
|
+
throw new InvalidAssetNameError('DEPIN assets are only available in testnet', name);
|
|
3628
3791
|
}
|
|
3629
3792
|
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3793
|
+
ensureMaxLength(name, getMaxAssetNameLength(network || 'xna-test'), 'DEPIN asset name');
|
|
3794
|
+
|
|
3795
|
+
if (name !== name.toUpperCase()) {
|
|
3796
|
+
throw new InvalidAssetNameError('DEPIN asset name must be uppercase', name);
|
|
3633
3797
|
}
|
|
3634
3798
|
|
|
3635
|
-
|
|
3636
|
-
if (rootPart.length < ASSET_NAME_RULES.DEPIN.minLength) {
|
|
3799
|
+
if (!isDepinIndicator(name)) {
|
|
3637
3800
|
throw new InvalidAssetNameError(
|
|
3638
|
-
|
|
3801
|
+
'DEPIN asset name can only contain A-Z, 0-9, underscore, period, and separator /',
|
|
3639
3802
|
name
|
|
3640
3803
|
);
|
|
3641
3804
|
}
|
|
3642
3805
|
|
|
3643
|
-
|
|
3644
|
-
throw new InvalidAssetNameError('DEPIN asset name must be uppercase', name);
|
|
3645
|
-
}
|
|
3806
|
+
const parts = name.split(ASSET_NAME_RULES.DEPIN.separator);
|
|
3646
3807
|
|
|
3647
|
-
if (
|
|
3808
|
+
if (parts.length > 1) {
|
|
3809
|
+
parts.forEach(part => {
|
|
3810
|
+
if (part.length < MIN_ASSET_LENGTH) {
|
|
3811
|
+
throw new InvalidAssetNameError(
|
|
3812
|
+
`Each DEPIN sub-part must be at least ${MIN_ASSET_LENGTH} characters`,
|
|
3813
|
+
name
|
|
3814
|
+
);
|
|
3815
|
+
}
|
|
3816
|
+
});
|
|
3817
|
+
} else if (name.length < MIN_ASSET_LENGTH + 1) {
|
|
3648
3818
|
throw new InvalidAssetNameError(
|
|
3649
|
-
|
|
3819
|
+
`DEPIN name must be at least ${MIN_ASSET_LENGTH} characters (excluding &)`,
|
|
3650
3820
|
name
|
|
3651
3821
|
);
|
|
3652
3822
|
}
|
|
3653
3823
|
|
|
3654
|
-
const subParts = parts.slice(1);
|
|
3655
|
-
subParts.forEach(part => {
|
|
3656
|
-
if (part.length < ASSET_NAME_RULES.DEPIN.minLength) {
|
|
3657
|
-
throw new InvalidAssetNameError(
|
|
3658
|
-
`Each DEPIN sub-part must be at least ${ASSET_NAME_RULES.DEPIN.minLength} characters`,
|
|
3659
|
-
name
|
|
3660
|
-
);
|
|
3661
|
-
}
|
|
3662
|
-
|
|
3663
|
-
if (part !== part.toUpperCase()) {
|
|
3664
|
-
throw new InvalidAssetNameError('DEPIN asset name must be uppercase', name);
|
|
3665
|
-
}
|
|
3666
|
-
|
|
3667
|
-
if (!ASSET_NAME_RULES.DEPIN.pattern.test(part)) {
|
|
3668
|
-
throw new InvalidAssetNameError(
|
|
3669
|
-
'DEPIN asset name can only contain A-Z, 0-9, underscore, and period',
|
|
3670
|
-
name
|
|
3671
|
-
);
|
|
3672
|
-
}
|
|
3673
|
-
});
|
|
3674
|
-
|
|
3675
3824
|
return true;
|
|
3676
3825
|
}
|
|
3677
3826
|
|
|
@@ -3679,24 +3828,31 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
3679
3828
|
* Validate owner token name
|
|
3680
3829
|
* Format: ASSETNAME!
|
|
3681
3830
|
*/
|
|
3682
|
-
static validateOwnerToken(name) {
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3831
|
+
static validateOwnerToken(name, network) {
|
|
3832
|
+
ensureName(name, 'Owner token name');
|
|
3833
|
+
|
|
3834
|
+
ensureMaxLength(name, getMaxAssetNameLength(network), 'Owner token name');
|
|
3686
3835
|
|
|
3687
3836
|
if (!name.endsWith('!')) {
|
|
3688
3837
|
throw new InvalidAssetNameError('Owner token must end with !', name);
|
|
3689
3838
|
}
|
|
3690
3839
|
|
|
3691
3840
|
const assetName = name.substring(0, name.length - 1);
|
|
3841
|
+
const validBaseName = isNameValidBeforeTag(assetName)
|
|
3842
|
+
|| (assetName.startsWith('&') && (() => {
|
|
3843
|
+
try {
|
|
3844
|
+
this.validateDepin(assetName, network);
|
|
3845
|
+
return true;
|
|
3846
|
+
} catch (error) {
|
|
3847
|
+
return false;
|
|
3848
|
+
}
|
|
3849
|
+
})());
|
|
3692
3850
|
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
} else {
|
|
3699
|
-
this.validateRoot(assetName);
|
|
3851
|
+
if (!validBaseName) {
|
|
3852
|
+
throw new InvalidAssetNameError(
|
|
3853
|
+
'Owner name contains invalid characters (Valid characters are: A-Z 0-9 _ .) (special characters can\'t be the first or last characters)',
|
|
3854
|
+
name
|
|
3855
|
+
);
|
|
3700
3856
|
}
|
|
3701
3857
|
|
|
3702
3858
|
return true;
|
|
@@ -3707,27 +3863,27 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
3707
3863
|
* @param {string} name - Asset name
|
|
3708
3864
|
* @returns {string} Asset type ('ROOT', 'SUB', 'UNIQUE', 'QUALIFIER', 'RESTRICTED', 'DEPIN', 'OWNER')
|
|
3709
3865
|
*/
|
|
3710
|
-
static validateAndDetectType(name) {
|
|
3866
|
+
static validateAndDetectType(name, network) {
|
|
3711
3867
|
if (name.endsWith('!')) {
|
|
3712
|
-
this.validateOwnerToken(name);
|
|
3868
|
+
this.validateOwnerToken(name, network);
|
|
3713
3869
|
return 'OWNER';
|
|
3714
3870
|
} else if (name.startsWith('#')) {
|
|
3715
|
-
this.validateQualifier(name);
|
|
3871
|
+
this.validateQualifier(name, network);
|
|
3716
3872
|
return name.includes('/') ? 'SUB_QUALIFIER' : 'QUALIFIER';
|
|
3717
3873
|
} else if (name.startsWith('$')) {
|
|
3718
|
-
this.validateRestricted(name);
|
|
3874
|
+
this.validateRestricted(name, network);
|
|
3719
3875
|
return 'RESTRICTED';
|
|
3720
3876
|
} else if (name.startsWith('&')) {
|
|
3721
|
-
this.validateDepin(name);
|
|
3877
|
+
this.validateDepin(name, network);
|
|
3722
3878
|
return 'DEPIN';
|
|
3723
3879
|
} else if (name.includes('#')) {
|
|
3724
|
-
this.validateUnique(name);
|
|
3880
|
+
this.validateUnique(name, network);
|
|
3725
3881
|
return 'UNIQUE';
|
|
3726
3882
|
} else if (name.includes('/')) {
|
|
3727
|
-
this.validateSub(name);
|
|
3883
|
+
this.validateSub(name, network);
|
|
3728
3884
|
return 'SUB';
|
|
3729
3885
|
} else {
|
|
3730
|
-
this.validateRoot(name);
|
|
3886
|
+
this.validateRoot(name, network);
|
|
3731
3887
|
return 'ROOT';
|
|
3732
3888
|
}
|
|
3733
3889
|
}
|
|
@@ -4296,14 +4452,20 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
4296
4452
|
}
|
|
4297
4453
|
|
|
4298
4454
|
/**
|
|
4299
|
-
* Estimate transaction fee
|
|
4300
|
-
*
|
|
4301
|
-
*
|
|
4455
|
+
* Estimate transaction fee.
|
|
4456
|
+
*
|
|
4457
|
+
* Both arguments accept either a count (legacy) or an array of descriptors
|
|
4458
|
+
* that lets the underlying estimator distinguish PQ AuthScript inputs/outputs
|
|
4459
|
+
* from legacy P2PKH ones. Pass arrays whenever you have actual UTXOs and
|
|
4460
|
+
* output addresses on hand — counts produce a legacy-only estimate.
|
|
4461
|
+
*
|
|
4462
|
+
* @param {number|Array} inputs - Input count or array of UTXO-like descriptors
|
|
4463
|
+
* @param {number|Array} outputs - Output count or array of address-like descriptors
|
|
4302
4464
|
* @returns {Promise<number>} Estimated fee in XNA
|
|
4303
4465
|
*/
|
|
4304
|
-
async estimateFee(
|
|
4466
|
+
async estimateFee(inputs, outputs) {
|
|
4305
4467
|
const feeRate = await this.utxoSelector.getFeeRate();
|
|
4306
|
-
return this.utxoSelector.estimateFee(
|
|
4468
|
+
return this.utxoSelector.estimateFee(inputs, outputs, feeRate);
|
|
4307
4469
|
}
|
|
4308
4470
|
|
|
4309
4471
|
/**
|
|
@@ -4404,22 +4566,22 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
4404
4566
|
|
|
4405
4567
|
switch (type) {
|
|
4406
4568
|
case 'ROOT':
|
|
4407
|
-
AssetNameValidator.validateRoot(assetName);
|
|
4569
|
+
AssetNameValidator.validateRoot(assetName, this.network);
|
|
4408
4570
|
break;
|
|
4409
4571
|
case 'SUB':
|
|
4410
|
-
AssetNameValidator.validateSub(assetName);
|
|
4572
|
+
AssetNameValidator.validateSub(assetName, this.network);
|
|
4411
4573
|
break;
|
|
4412
4574
|
case 'UNIQUE':
|
|
4413
|
-
AssetNameValidator.validateUnique(assetName);
|
|
4575
|
+
AssetNameValidator.validateUnique(assetName, this.network);
|
|
4414
4576
|
break;
|
|
4415
4577
|
case 'QUALIFIER':
|
|
4416
|
-
AssetNameValidator.validateQualifier(assetName);
|
|
4578
|
+
AssetNameValidator.validateQualifier(assetName, this.network);
|
|
4417
4579
|
break;
|
|
4418
4580
|
case 'RESTRICTED':
|
|
4419
|
-
AssetNameValidator.validateRestricted(assetName);
|
|
4581
|
+
AssetNameValidator.validateRestricted(assetName, this.network);
|
|
4420
4582
|
break;
|
|
4421
4583
|
case 'DEPIN':
|
|
4422
|
-
AssetNameValidator.validateDepin(assetName);
|
|
4584
|
+
AssetNameValidator.validateDepin(assetName, this.network);
|
|
4423
4585
|
break;
|
|
4424
4586
|
default:
|
|
4425
4587
|
throw new Error(`Unknown asset type: ${type}`);
|
|
@@ -4765,7 +4927,8 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
4765
4927
|
const changeAddress = await this.getChangeAddress();
|
|
4766
4928
|
|
|
4767
4929
|
// 5. Estimate fee (rough estimate for initial UTXO selection)
|
|
4768
|
-
const
|
|
4930
|
+
const outputAddresses = [burnInfo.address, changeAddress, toAddress];
|
|
4931
|
+
const estimatedFee = await this.estimateFee(1, outputAddresses);
|
|
4769
4932
|
|
|
4770
4933
|
// 6. Calculate total XNA needed
|
|
4771
4934
|
const totalXNANeeded = burnInfo.amount + estimatedFee;
|
|
@@ -4775,8 +4938,8 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
4775
4938
|
const baseCurrencyUTXOs = utxoSelection.xnaUTXOs;
|
|
4776
4939
|
const totalXNAInput = utxoSelection.totalXNA;
|
|
4777
4940
|
|
|
4778
|
-
// 8. Recalculate fee with actual
|
|
4779
|
-
const actualFee = await this.estimateFee(baseCurrencyUTXOs
|
|
4941
|
+
// 8. Recalculate fee with actual inputs (PQ-aware)
|
|
4942
|
+
const actualFee = await this.estimateFee(baseCurrencyUTXOs, outputAddresses);
|
|
4780
4943
|
|
|
4781
4944
|
// 9. Verify we still have enough after fee recalculation
|
|
4782
4945
|
const totalRequired = burnInfo.amount + actualFee;
|
|
@@ -5000,7 +5163,13 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
5000
5163
|
// 8. Estimate fee
|
|
5001
5164
|
// Inputs: XNA UTXOs + owner token UTXO
|
|
5002
5165
|
// Outputs: burn + change + owner token return + issue operation
|
|
5003
|
-
const
|
|
5166
|
+
const outputAddresses = [
|
|
5167
|
+
burnInfo.address,
|
|
5168
|
+
changeAddress,
|
|
5169
|
+
changeAddress, // owner token return goes to change address
|
|
5170
|
+
toAddress,
|
|
5171
|
+
];
|
|
5172
|
+
const estimatedFee = await this.estimateFee(2, outputAddresses);
|
|
5004
5173
|
|
|
5005
5174
|
// 9. Calculate total XNA needed
|
|
5006
5175
|
const totalXNANeeded = burnInfo.amount + estimatedFee;
|
|
@@ -5010,9 +5179,9 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
5010
5179
|
const baseCurrencyUTXOs = utxoSelection.xnaUTXOs;
|
|
5011
5180
|
const totalXNAInput = utxoSelection.totalXNA;
|
|
5012
5181
|
|
|
5013
|
-
// 11. Recalculate fee with actual
|
|
5014
|
-
const
|
|
5015
|
-
const actualFee = await this.estimateFee(
|
|
5182
|
+
// 11. Recalculate fee with actual inputs (PQ-aware), including owner token UTXO
|
|
5183
|
+
const actualFeeInputs = [...baseCurrencyUTXOs, ownerTokenUTXO];
|
|
5184
|
+
const actualFee = await this.estimateFee(actualFeeInputs, outputAddresses);
|
|
5016
5185
|
|
|
5017
5186
|
// 12. Verify we have enough XNA
|
|
5018
5187
|
const totalRequired = burnInfo.amount + actualFee;
|
|
@@ -5214,14 +5383,15 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
5214
5383
|
const toAddress = await this.getToAddress();
|
|
5215
5384
|
const changeAddress = await this.getChangeAddress();
|
|
5216
5385
|
|
|
5217
|
-
const
|
|
5386
|
+
const outputAddresses = [burnInfo.address, changeAddress, toAddress];
|
|
5387
|
+
const estimatedFee = await this.estimateFee(1, outputAddresses);
|
|
5218
5388
|
const totalXNANeeded = burnInfo.amount + estimatedFee;
|
|
5219
5389
|
|
|
5220
5390
|
const utxoSelection = await this.selectUTXOs(totalXNANeeded, null, 0);
|
|
5221
5391
|
const baseCurrencyUTXOs = utxoSelection.xnaUTXOs;
|
|
5222
5392
|
const totalXNAInput = utxoSelection.totalXNA;
|
|
5223
5393
|
|
|
5224
|
-
const actualFee = await this.estimateFee(baseCurrencyUTXOs
|
|
5394
|
+
const actualFee = await this.estimateFee(baseCurrencyUTXOs, outputAddresses);
|
|
5225
5395
|
const totalRequired = burnInfo.amount + actualFee;
|
|
5226
5396
|
|
|
5227
5397
|
if (totalXNAInput < totalRequired) {
|
|
@@ -5436,9 +5606,14 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
5436
5606
|
|
|
5437
5607
|
// 8. Estimate fee
|
|
5438
5608
|
// Inputs: XNA UTXOs + owner token UTXO
|
|
5439
|
-
// Outputs: burn + change + reissue operation
|
|
5440
|
-
|
|
5441
|
-
|
|
5609
|
+
// Outputs: burn + change + owner token return + reissue operation
|
|
5610
|
+
const outputAddresses = [
|
|
5611
|
+
burnInfo.address,
|
|
5612
|
+
changeAddress,
|
|
5613
|
+
changeAddress, // owner token return goes to change address
|
|
5614
|
+
toAddress,
|
|
5615
|
+
];
|
|
5616
|
+
const estimatedFee = await this.estimateFee(2, outputAddresses);
|
|
5442
5617
|
|
|
5443
5618
|
// 9. Calculate total XNA needed
|
|
5444
5619
|
const totalXNANeeded = burnInfo.amount + estimatedFee;
|
|
@@ -5448,9 +5623,9 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
5448
5623
|
const baseCurrencyUTXOs = utxoSelection.xnaUTXOs;
|
|
5449
5624
|
const totalXNAInput = utxoSelection.totalXNA;
|
|
5450
5625
|
|
|
5451
|
-
// 11. Recalculate fee with actual
|
|
5452
|
-
const
|
|
5453
|
-
const actualFee = await this.estimateFee(
|
|
5626
|
+
// 11. Recalculate fee with actual inputs (PQ-aware), including owner token UTXO
|
|
5627
|
+
const actualFeeInputs = [...baseCurrencyUTXOs, ownerTokenUTXO];
|
|
5628
|
+
const actualFee = await this.estimateFee(actualFeeInputs, outputAddresses);
|
|
5454
5629
|
|
|
5455
5630
|
// 12. Verify we have enough XNA
|
|
5456
5631
|
const totalRequired = burnInfo.amount + actualFee;
|
|
@@ -5711,7 +5886,13 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
5711
5886
|
// 7. Estimate fee
|
|
5712
5887
|
// Inputs: XNA UTXOs + owner token UTXO
|
|
5713
5888
|
// Outputs: burn + change + owner token return + issue_unique operation
|
|
5714
|
-
const
|
|
5889
|
+
const outputAddresses = [
|
|
5890
|
+
burnInfo.address,
|
|
5891
|
+
changeAddress,
|
|
5892
|
+
changeAddress, // owner token return goes to change address
|
|
5893
|
+
toAddress,
|
|
5894
|
+
];
|
|
5895
|
+
const estimatedFee = await this.estimateFee(2, outputAddresses);
|
|
5715
5896
|
|
|
5716
5897
|
// 8. Calculate total XNA needed
|
|
5717
5898
|
const totalXNANeeded = burnInfo.amount + estimatedFee;
|
|
@@ -5721,9 +5902,9 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
5721
5902
|
const baseCurrencyUTXOs = utxoSelection.xnaUTXOs;
|
|
5722
5903
|
const totalXNAInput = utxoSelection.totalXNA;
|
|
5723
5904
|
|
|
5724
|
-
// 10. Recalculate fee with actual
|
|
5725
|
-
const
|
|
5726
|
-
const actualFee = await this.estimateFee(
|
|
5905
|
+
// 10. Recalculate fee with actual inputs (PQ-aware), including owner token UTXO
|
|
5906
|
+
const actualFeeInputs = [...baseCurrencyUTXOs, ownerTokenUTXO];
|
|
5907
|
+
const actualFee = await this.estimateFee(actualFeeInputs, outputAddresses);
|
|
5727
5908
|
|
|
5728
5909
|
// 11. Verify we have enough XNA
|
|
5729
5910
|
const totalRequired = burnInfo.amount + actualFee;
|
|
@@ -5840,7 +6021,7 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
5840
6021
|
*
|
|
5841
6022
|
* QUALIFIER assets:
|
|
5842
6023
|
* - KYC/compliance tags (e.g., #KYC_VERIFIED, #ACCREDITED)
|
|
5843
|
-
* - Format: #NAME or #ROOT
|
|
6024
|
+
* - Format: #NAME or #ROOT/#SUB
|
|
5844
6025
|
* - Cost: 2000 XNA (root) or 200 XNA (sub-qualifier)
|
|
5845
6026
|
* - Quantity: 1-10 units only
|
|
5846
6027
|
* - Units: Always 0 (non-divisible)
|
|
@@ -5971,8 +6152,8 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
5971
6152
|
const changeAddress = await this.getChangeAddress();
|
|
5972
6153
|
|
|
5973
6154
|
// 7. Estimate fee
|
|
5974
|
-
const
|
|
5975
|
-
const estimatedFee = await this.estimateFee(2,
|
|
6155
|
+
const outputAddresses = [burnInfo.address, changeAddress, toAddress];
|
|
6156
|
+
const estimatedFee = await this.estimateFee(2, outputAddresses);
|
|
5976
6157
|
|
|
5977
6158
|
// 8. Calculate total XNA needed
|
|
5978
6159
|
const totalXNANeeded = burnInfo.amount + estimatedFee;
|
|
@@ -5982,9 +6163,9 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
5982
6163
|
const baseCurrencyUTXOs = utxoSelection.xnaUTXOs;
|
|
5983
6164
|
const totalXNAInput = utxoSelection.totalXNA;
|
|
5984
6165
|
|
|
5985
|
-
// 10. Recalculate fee with actual
|
|
5986
|
-
const
|
|
5987
|
-
const actualFee = await this.estimateFee(
|
|
6166
|
+
// 10. Recalculate fee with actual inputs (PQ-aware), including parent qualifier UTXOs
|
|
6167
|
+
const actualFeeInputs = [...baseCurrencyUTXOs, ...parentQualifierUTXOs];
|
|
6168
|
+
const actualFee = await this.estimateFee(actualFeeInputs, outputAddresses);
|
|
5988
6169
|
|
|
5989
6170
|
// 11. Verify we have enough XNA
|
|
5990
6171
|
const totalRequired = burnInfo.amount + actualFee;
|
|
@@ -6223,7 +6404,13 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
6223
6404
|
}
|
|
6224
6405
|
|
|
6225
6406
|
// 7. Estimate fee (+1 for owner token input)
|
|
6226
|
-
const
|
|
6407
|
+
const outputAddresses = [
|
|
6408
|
+
burnInfo.address,
|
|
6409
|
+
changeAddress,
|
|
6410
|
+
changeAddress, // owner token return goes to change address
|
|
6411
|
+
toAddress,
|
|
6412
|
+
];
|
|
6413
|
+
const estimatedFee = await this.estimateFee(2, outputAddresses);
|
|
6227
6414
|
|
|
6228
6415
|
// 8. Calculate total XNA needed
|
|
6229
6416
|
const totalXNANeeded = burnInfo.amount + estimatedFee;
|
|
@@ -6233,8 +6420,9 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
6233
6420
|
const baseCurrencyUTXOs = utxoSelection.xnaUTXOs;
|
|
6234
6421
|
const totalXNAInput = utxoSelection.totalXNA;
|
|
6235
6422
|
|
|
6236
|
-
// 10. Recalculate fee with actual
|
|
6237
|
-
const
|
|
6423
|
+
// 10. Recalculate fee with actual inputs (PQ-aware), including owner token UTXO
|
|
6424
|
+
const actualFeeInputs = [...baseCurrencyUTXOs, ownerTokenUTXO];
|
|
6425
|
+
const actualFee = await this.estimateFee(actualFeeInputs, outputAddresses);
|
|
6238
6426
|
|
|
6239
6427
|
// 11. Verify we have enough XNA
|
|
6240
6428
|
const totalRequired = burnInfo.amount + actualFee;
|
|
@@ -6491,7 +6679,13 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
6491
6679
|
const burnInfo = this.burnManager.getReissueBurn();
|
|
6492
6680
|
|
|
6493
6681
|
// 8. Estimate fee
|
|
6494
|
-
const
|
|
6682
|
+
const outputAddresses = [
|
|
6683
|
+
burnInfo.address,
|
|
6684
|
+
changeAddress,
|
|
6685
|
+
changeAddress, // owner token return goes to change address
|
|
6686
|
+
toAddress,
|
|
6687
|
+
];
|
|
6688
|
+
const estimatedFee = await this.estimateFee(2, outputAddresses);
|
|
6495
6689
|
|
|
6496
6690
|
// 9. Calculate total XNA needed
|
|
6497
6691
|
const totalXNANeeded = burnInfo.amount + estimatedFee;
|
|
@@ -6501,9 +6695,9 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
6501
6695
|
const baseCurrencyUTXOs = utxoSelection.xnaUTXOs;
|
|
6502
6696
|
const totalXNAInput = utxoSelection.totalXNA;
|
|
6503
6697
|
|
|
6504
|
-
// 11. Recalculate fee with actual
|
|
6505
|
-
const
|
|
6506
|
-
const actualFee = await this.estimateFee(
|
|
6698
|
+
// 11. Recalculate fee with actual inputs (PQ-aware), including owner token UTXO
|
|
6699
|
+
const actualFeeInputs = [...baseCurrencyUTXOs, ownerTokenUTXO];
|
|
6700
|
+
const actualFee = await this.estimateFee(actualFeeInputs, outputAddresses);
|
|
6507
6701
|
|
|
6508
6702
|
// 12. Verify we have enough XNA
|
|
6509
6703
|
const totalRequired = burnInfo.amount + actualFee;
|
|
@@ -6738,7 +6932,9 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
6738
6932
|
: this.burnManager.getTagAddressBurn(addressCount);
|
|
6739
6933
|
|
|
6740
6934
|
// 6. Estimate fee
|
|
6741
|
-
|
|
6935
|
+
// Outputs: burn + XNA change + tag/untag operation (sent to changeAddress)
|
|
6936
|
+
const outputAddresses = [burnInfo.address, changeAddress, changeAddress];
|
|
6937
|
+
const estimatedFee = await this.estimateFee(2, outputAddresses);
|
|
6742
6938
|
|
|
6743
6939
|
// 7. Calculate total XNA needed
|
|
6744
6940
|
const totalXNANeeded = burnInfo.amount + estimatedFee;
|
|
@@ -6748,9 +6944,9 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
6748
6944
|
const baseCurrencyUTXOs = utxoSelection.xnaUTXOs;
|
|
6749
6945
|
const totalXNAInput = utxoSelection.totalXNA;
|
|
6750
6946
|
|
|
6751
|
-
// 9. Recalculate fee with actual
|
|
6752
|
-
const
|
|
6753
|
-
const actualFee = await this.estimateFee(
|
|
6947
|
+
// 9. Recalculate fee with actual inputs (PQ-aware), including qualifier UTXOs
|
|
6948
|
+
const actualFeeInputs = [...baseCurrencyUTXOs, ...qualifierUTXOs];
|
|
6949
|
+
const actualFee = await this.estimateFee(actualFeeInputs, outputAddresses);
|
|
6754
6950
|
|
|
6755
6951
|
// 10. Verify we have enough XNA
|
|
6756
6952
|
const totalRequired = burnInfo.amount + actualFee;
|
|
@@ -6985,16 +7181,18 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
6985
7181
|
const burnAmount = 0;
|
|
6986
7182
|
|
|
6987
7183
|
// 6. Estimate fee
|
|
6988
|
-
|
|
7184
|
+
// Outputs: XNA change + freeze/unfreeze operation (sent to changeAddress)
|
|
7185
|
+
const outputAddresses = [changeAddress, changeAddress];
|
|
7186
|
+
const estimatedFee = await this.estimateFee(2, outputAddresses);
|
|
6989
7187
|
|
|
6990
7188
|
// 7. Select XNA UTXOs (only for fee, no burn)
|
|
6991
7189
|
const utxoSelection = await this.selectUTXOs(estimatedFee, null, 0);
|
|
6992
7190
|
const baseCurrencyUTXOs = utxoSelection.xnaUTXOs;
|
|
6993
7191
|
const totalXNAInput = utxoSelection.totalXNA;
|
|
6994
7192
|
|
|
6995
|
-
// 8. Recalculate fee with actual
|
|
6996
|
-
const
|
|
6997
|
-
const actualFee = await this.estimateFee(
|
|
7193
|
+
// 8. Recalculate fee with actual inputs (PQ-aware), including owner token UTXO
|
|
7194
|
+
const actualFeeInputs = [...baseCurrencyUTXOs, ownerTokenUTXO];
|
|
7195
|
+
const actualFee = await this.estimateFee(actualFeeInputs, outputAddresses);
|
|
6998
7196
|
|
|
6999
7197
|
// 9. Verify we have enough XNA for fee
|
|
7000
7198
|
if (totalXNAInput < actualFee) {
|
|
@@ -7305,7 +7503,7 @@ var NeuraiAssetsBundle = (function (exports) {
|
|
|
7305
7503
|
/**
|
|
7306
7504
|
* Create a ROOT asset
|
|
7307
7505
|
* @param {object} params - Asset creation parameters
|
|
7308
|
-
|
|
7506
|
+
* @param {string} params.assetName - Asset name (3-31 visible chars on mainnet, A-Z 0-9 _ .)
|
|
7309
7507
|
* @param {number} params.quantity - Total supply
|
|
7310
7508
|
* @param {number} [params.units=0] - Decimal places (0-8)
|
|
7311
7509
|
* @param {boolean} [params.reissuable=true] - Can mint more later
|