@neuraiproject/neurai-assets 1.2.3 → 1.2.5

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.
@@ -1091,45 +1091,45 @@ var NeuraiAssetsBundle = (function (exports) {
1091
1091
  };
1092
1092
 
1093
1093
  /**
1094
- * Asset naming rules (same for all networks)
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: 30,
1100
+ maxLength: 31,
1100
1101
  pattern: /^[A-Z0-9_.]+$/,
1101
- cannotStartWith: ['.', 'A', 'Z'],
1102
- reserved: ['XNA', 'NEURAI']
1102
+ reserved: ['XNA', 'NEURAI', 'NEURAICOIN']
1103
1103
  },
1104
1104
  SUB: {
1105
1105
  minLength: 1,
1106
- maxLength: 30,
1106
+ maxLength: 31,
1107
1107
  pattern: /^[A-Z0-9_.]+$/,
1108
1108
  separator: '/',
1109
- maxDepth: 1 // Only one level of sub-assets
1109
+ maxDepth: null
1110
1110
  },
1111
1111
  UNIQUE: {
1112
1112
  minLength: 1,
1113
- maxLength: 30,
1114
- pattern: /^[A-Z0-9_.]+$/,
1113
+ maxLength: 32,
1114
+ pattern: /^[-A-Za-z0-9@$%&*()[\]{}_.?:]+$/,
1115
1115
  separator: '#'
1116
1116
  },
1117
1117
  QUALIFIER: {
1118
1118
  minLength: 3,
1119
- maxLength: 30,
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: 30,
1126
+ maxLength: 32,
1127
1127
  pattern: /^[A-Z0-9_.]+$/,
1128
1128
  prefix: '$'
1129
1129
  },
1130
1130
  DEPIN: {
1131
1131
  minLength: 3,
1132
- maxLength: 120,
1132
+ maxLength: 121,
1133
1133
  pattern: /^[A-Z0-9_.]+$/,
1134
1134
  prefix: '&',
1135
1135
  separator: '/'
@@ -3370,52 +3370,164 @@ var NeuraiAssetsBundle = (function (exports) {
3370
3370
  const { ASSET_NAME_RULES } = requireConstants();
3371
3371
  const { InvalidAssetNameError } = requireErrors();
3372
3372
 
3373
+ const MIN_ASSET_LENGTH = 3;
3374
+ const MAINNET_MAX_NAME_LENGTH = 32;
3375
+ const TESTNET_MAX_NAME_LENGTH = 121;
3376
+
3377
+ const ROOT_NAME_CHARACTERS = /^[A-Z0-9._]{3,}$/;
3378
+ const SUB_NAME_CHARACTERS = /^[A-Z0-9._]+$/;
3379
+ const UNIQUE_TAG_CHARACTERS = /^[-A-Za-z0-9@$%&*()[\]{}_.?:]+$/;
3380
+ const QUALIFIER_NAME_CHARACTERS = /^#[A-Z0-9._]{3,}$/;
3381
+ const SUB_QUALIFIER_NAME_CHARACTERS = /^#[A-Z0-9._]+$/;
3382
+ const RESTRICTED_NAME_CHARACTERS = /^\$[A-Z0-9._]{3,}$/;
3383
+ const DEPIN_NAME_CHARACTERS = /^&[A-Z0-9._]{3,}$/;
3384
+ const SUB_DEPIN_NAME_CHARACTERS = /^&[A-Z0-9._]+\/[A-Z0-9._/]+$/;
3385
+ const DOUBLE_PUNCTUATION = /^.*[._]{2,}.*$/;
3386
+ const LEADING_PUNCTUATION = /^[._].*$/;
3387
+ const TRAILING_PUNCTUATION = /^.*[._]$/;
3388
+ const QUALIFIER_LEADING_PUNCTUATION = /^[#$][._].*$/;
3389
+ const NEURAI_NAMES = /^XNA$|^NEURAI$|^NEURAICOIN$|^#XNA$|^#NEURAI$|^#NEURAICOIN$/;
3390
+
3391
+ function isTestnet(network) {
3392
+ return typeof network === 'string' && network.toLowerCase().includes('test');
3393
+ }
3394
+
3395
+ function getMaxAssetNameLength(network) {
3396
+ return isTestnet(network) ? TESTNET_MAX_NAME_LENGTH : MAINNET_MAX_NAME_LENGTH;
3397
+ }
3398
+
3399
+ function getRootOrSubMaxLength(network) {
3400
+ return getMaxAssetNameLength(network) - 1;
3401
+ }
3402
+
3403
+ function ensureName(name, label) {
3404
+ if (!name || typeof name !== 'string') {
3405
+ throw new InvalidAssetNameError(`${label} must be a non-empty string`, name);
3406
+ }
3407
+ }
3408
+
3409
+ function ensureMaxLength(name, maxLength, label) {
3410
+ if (name.length > maxLength) {
3411
+ throw new InvalidAssetNameError(`${label} cannot exceed ${maxLength} characters`, name);
3412
+ }
3413
+ }
3414
+
3415
+ function ensureUppercase(name, message) {
3416
+ if (name !== name.toUpperCase()) {
3417
+ throw new InvalidAssetNameError(message, name);
3418
+ }
3419
+ }
3420
+
3421
+ function isRootNameValid(name) {
3422
+ return ROOT_NAME_CHARACTERS.test(name)
3423
+ && !DOUBLE_PUNCTUATION.test(name)
3424
+ && !LEADING_PUNCTUATION.test(name)
3425
+ && !TRAILING_PUNCTUATION.test(name)
3426
+ && !NEURAI_NAMES.test(name);
3427
+ }
3428
+
3429
+ function isSubNameValid(name) {
3430
+ return SUB_NAME_CHARACTERS.test(name)
3431
+ && !DOUBLE_PUNCTUATION.test(name)
3432
+ && !LEADING_PUNCTUATION.test(name)
3433
+ && !TRAILING_PUNCTUATION.test(name);
3434
+ }
3435
+
3436
+ function isQualifierNameValid(name) {
3437
+ return QUALIFIER_NAME_CHARACTERS.test(name)
3438
+ && !DOUBLE_PUNCTUATION.test(name)
3439
+ && !QUALIFIER_LEADING_PUNCTUATION.test(name)
3440
+ && !TRAILING_PUNCTUATION.test(name)
3441
+ && !NEURAI_NAMES.test(name);
3442
+ }
3443
+
3444
+ function isSubQualifierNameValid(name) {
3445
+ return SUB_QUALIFIER_NAME_CHARACTERS.test(name)
3446
+ && !DOUBLE_PUNCTUATION.test(name)
3447
+ && !LEADING_PUNCTUATION.test(name)
3448
+ && !TRAILING_PUNCTUATION.test(name);
3449
+ }
3450
+
3451
+ function isRestrictedNameValid(name) {
3452
+ return RESTRICTED_NAME_CHARACTERS.test(name)
3453
+ && !DOUBLE_PUNCTUATION.test(name)
3454
+ && !LEADING_PUNCTUATION.test(name)
3455
+ && !TRAILING_PUNCTUATION.test(name)
3456
+ && !NEURAI_NAMES.test(name);
3457
+ }
3458
+
3459
+ function isNameValidBeforeTag(name) {
3460
+ const parts = name.split('/');
3461
+
3462
+ if (!isRootNameValid(parts[0])) {
3463
+ return false;
3464
+ }
3465
+
3466
+ for (let index = 1; index < parts.length; index += 1) {
3467
+ if (!isSubNameValid(parts[index])) {
3468
+ return false;
3469
+ }
3470
+ }
3471
+
3472
+ return true;
3473
+ }
3474
+
3475
+ function isQualifierNameValidBeforeTag(name) {
3476
+ const parts = name.split('/');
3477
+
3478
+ if (!isQualifierNameValid(parts[0])) {
3479
+ return false;
3480
+ }
3481
+
3482
+ if (parts.length > 2) {
3483
+ return false;
3484
+ }
3485
+
3486
+ for (let index = 1; index < parts.length; index += 1) {
3487
+ if (!isSubQualifierNameValid(parts[index])) {
3488
+ return false;
3489
+ }
3490
+ }
3491
+
3492
+ return true;
3493
+ }
3494
+
3495
+ function isDepinIndicator(name) {
3496
+ return DEPIN_NAME_CHARACTERS.test(name) || SUB_DEPIN_NAME_CHARACTERS.test(name);
3497
+ }
3498
+
3373
3499
  class AssetNameValidator {
3374
3500
  /**
3375
3501
  * Validate ROOT asset name
3376
- * Rules: 3-30 uppercase characters, A-Z, 0-9, underscore, period
3377
- * Cannot start with period, A, or Z
3502
+ * Rules: 3-31 visible characters on mainnet, A-Z, 0-9, underscore, period
3503
+ * Cannot start or end with period/underscore, cannot use repeated punctuation
3378
3504
  * Cannot be reserved names
3379
3505
  */
3380
- static validateRoot(name) {
3381
- if (!name || typeof name !== 'string') {
3382
- throw new InvalidAssetNameError('Asset name must be a non-empty string', name);
3383
- }
3506
+ static validateRoot(name, network = 'xna') {
3507
+ ensureName(name, 'Asset name');
3508
+
3509
+ const maxLength = getRootOrSubMaxLength(network);
3384
3510
 
3385
- // Length check
3386
- if (name.length < ASSET_NAME_RULES.ROOT.minLength || name.length > ASSET_NAME_RULES.ROOT.maxLength) {
3511
+ if (name.length < MIN_ASSET_LENGTH || name.length > maxLength) {
3387
3512
  throw new InvalidAssetNameError(
3388
- `ROOT asset name must be ${ASSET_NAME_RULES.ROOT.minLength}-${ASSET_NAME_RULES.ROOT.maxLength} characters`,
3513
+ `ROOT asset name must be ${MIN_ASSET_LENGTH}-${maxLength} characters`,
3389
3514
  name
3390
3515
  );
3391
3516
  }
3392
3517
 
3393
- // Uppercase check
3394
- if (name !== name.toUpperCase()) {
3395
- throw new InvalidAssetNameError('Asset name must be uppercase', name);
3396
- }
3518
+ ensureUppercase(name, 'Asset name must be uppercase');
3397
3519
 
3398
- // Starting character check
3399
- if (ASSET_NAME_RULES.ROOT.cannotStartWith.some(char => name.startsWith(char))) {
3400
- throw new InvalidAssetNameError(
3401
- `Asset name cannot start with: ${ASSET_NAME_RULES.ROOT.cannotStartWith.join(', ')}`,
3402
- name
3403
- );
3520
+ if (NEURAI_NAMES.test(name)) {
3521
+ throw new InvalidAssetNameError(`${name} is a reserved asset name`, name);
3404
3522
  }
3405
3523
 
3406
- // Pattern check
3407
- if (!ASSET_NAME_RULES.ROOT.pattern.test(name)) {
3524
+ if (!isRootNameValid(name)) {
3408
3525
  throw new InvalidAssetNameError(
3409
- 'Asset name can only contain A-Z, 0-9, underscore, and period',
3526
+ 'Name contains invalid characters (Valid characters are: A-Z 0-9 _ .) (special characters can\'t be the first or last characters)',
3410
3527
  name
3411
3528
  );
3412
3529
  }
3413
3530
 
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
3531
  return true;
3420
3532
  }
3421
3533
 
@@ -3423,39 +3535,25 @@ var NeuraiAssetsBundle = (function (exports) {
3423
3535
  * Validate SUB asset name
3424
3536
  * Format: ROOT/SUBNAME
3425
3537
  */
3426
- static validateSub(name) {
3427
- if (!name || typeof name !== 'string') {
3428
- throw new InvalidAssetNameError('SUB asset name must be a non-empty string', name);
3429
- }
3538
+ static validateSub(name, network = 'xna') {
3539
+ ensureName(name, 'SUB asset name');
3430
3540
 
3431
- const parts = name.split(ASSET_NAME_RULES.SUB.separator);
3432
- if (parts.length !== 2) {
3541
+ if (!name.includes(ASSET_NAME_RULES.SUB.separator)) {
3433
3542
  throw new InvalidAssetNameError(
3434
3543
  `SUB asset must be in ${ASSET_NAME_RULES.SUB.separator} format (ROOT/SUBNAME)`,
3435
3544
  name
3436
3545
  );
3437
3546
  }
3438
3547
 
3439
- const [rootName, subName] = parts;
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
- }
3548
+ ensureMaxLength(name, getRootOrSubMaxLength(network), 'SUB asset name');
3451
3549
 
3452
- if (subName !== subName.toUpperCase()) {
3550
+ if (name !== name.toUpperCase()) {
3453
3551
  throw new InvalidAssetNameError('SUB asset name must be uppercase', name);
3454
3552
  }
3455
3553
 
3456
- if (!ASSET_NAME_RULES.SUB.pattern.test(subName)) {
3554
+ if (!isNameValidBeforeTag(name)) {
3457
3555
  throw new InvalidAssetNameError(
3458
- 'SUB asset name can only contain A-Z, 0-9, underscore, and period',
3556
+ 'Name contains invalid characters (Valid characters are: A-Z 0-9 _ .) (special characters can\'t be the first or last characters)',
3459
3557
  name
3460
3558
  );
3461
3559
  }
@@ -3467,10 +3565,10 @@ var NeuraiAssetsBundle = (function (exports) {
3467
3565
  * Validate UNIQUE asset name
3468
3566
  * Format: ROOT#TAG
3469
3567
  */
3470
- static validateUnique(name) {
3471
- if (!name || typeof name !== 'string') {
3472
- throw new InvalidAssetNameError('UNIQUE asset name must be a non-empty string', name);
3473
- }
3568
+ static validateUnique(name, network = 'xna') {
3569
+ ensureName(name, 'UNIQUE asset name');
3570
+
3571
+ ensureMaxLength(name, getMaxAssetNameLength(network), 'UNIQUE asset name');
3474
3572
 
3475
3573
  const parts = name.split(ASSET_NAME_RULES.UNIQUE.separator);
3476
3574
  if (parts.length !== 2) {
@@ -3482,24 +3580,9 @@ var NeuraiAssetsBundle = (function (exports) {
3482
3580
 
3483
3581
  const [rootName, tag] = parts;
3484
3582
 
3485
- // Validate root part
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)) {
3583
+ if (!isNameValidBeforeTag(rootName) || !UNIQUE_TAG_CHARACTERS.test(tag)) {
3501
3584
  throw new InvalidAssetNameError(
3502
- 'UNIQUE tag can only contain A-Z, 0-9, underscore, and period',
3585
+ 'Unique name contains invalid characters (Valid characters are: A-Z a-z 0-9 @ $ % & * ( ) [ ] { } _ . ? : -)',
3503
3586
  name
3504
3587
  );
3505
3588
  }
@@ -3511,10 +3594,8 @@ var NeuraiAssetsBundle = (function (exports) {
3511
3594
  * Validate QUALIFIER asset name
3512
3595
  * Format: #NAME or #ROOT/SUB
3513
3596
  */
3514
- static validateQualifier(name) {
3515
- if (!name || typeof name !== 'string') {
3516
- throw new InvalidAssetNameError('QUALIFIER asset name must be a non-empty string', name);
3517
- }
3597
+ static validateQualifier(name, network = 'xna') {
3598
+ ensureName(name, 'QUALIFIER asset name');
3518
3599
 
3519
3600
  if (!name.startsWith(ASSET_NAME_RULES.QUALIFIER.prefix)) {
3520
3601
  throw new InvalidAssetNameError(
@@ -3523,58 +3604,17 @@ var NeuraiAssetsBundle = (function (exports) {
3523
3604
  );
3524
3605
  }
3525
3606
 
3526
- const withoutPrefix = name.substring(1);
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
- }
3607
+ ensureMaxLength(name, getMaxAssetNameLength(network), 'QUALIFIER asset name');
3537
3608
 
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
- }
3546
-
3547
- if (part !== part.toUpperCase()) {
3548
- throw new InvalidAssetNameError('QUALIFIER name must be uppercase', name);
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
- }
3609
+ if (name !== name.toUpperCase()) {
3610
+ throw new InvalidAssetNameError('QUALIFIER name must be uppercase', name);
3611
+ }
3571
3612
 
3572
- if (!ASSET_NAME_RULES.QUALIFIER.pattern.test(withoutPrefix)) {
3573
- throw new InvalidAssetNameError(
3574
- 'QUALIFIER name can only contain A-Z, 0-9, and underscore',
3575
- name
3576
- );
3577
- }
3613
+ if (!isQualifierNameValidBeforeTag(name)) {
3614
+ throw new InvalidAssetNameError(
3615
+ '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)',
3616
+ name
3617
+ );
3578
3618
  }
3579
3619
 
3580
3620
  return true;
@@ -3584,10 +3624,8 @@ var NeuraiAssetsBundle = (function (exports) {
3584
3624
  * Validate RESTRICTED asset name
3585
3625
  * Format: $NAME
3586
3626
  */
3587
- static validateRestricted(name) {
3588
- if (!name || typeof name !== 'string') {
3589
- throw new InvalidAssetNameError('RESTRICTED asset name must be a non-empty string', name);
3590
- }
3627
+ static validateRestricted(name, network = 'xna') {
3628
+ ensureName(name, 'RESTRICTED asset name');
3591
3629
 
3592
3630
  if (!name.startsWith(ASSET_NAME_RULES.RESTRICTED.prefix)) {
3593
3631
  throw new InvalidAssetNameError(
@@ -3596,10 +3634,18 @@ var NeuraiAssetsBundle = (function (exports) {
3596
3634
  );
3597
3635
  }
3598
3636
 
3599
- const withoutPrefix = name.substring(1);
3637
+ ensureMaxLength(name, getMaxAssetNameLength(network), 'RESTRICTED asset name');
3600
3638
 
3601
- // Validate the part after $ as a ROOT asset name
3602
- this.validateRoot(withoutPrefix);
3639
+ if (name !== name.toUpperCase()) {
3640
+ throw new InvalidAssetNameError('RESTRICTED asset name must be uppercase', name);
3641
+ }
3642
+
3643
+ if (!isRestrictedNameValid(name)) {
3644
+ throw new InvalidAssetNameError(
3645
+ '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)',
3646
+ name
3647
+ );
3648
+ }
3603
3649
 
3604
3650
  return true;
3605
3651
  }
@@ -3608,10 +3654,8 @@ var NeuraiAssetsBundle = (function (exports) {
3608
3654
  * Validate DEPIN asset name
3609
3655
  * Format: &NAME or &NAME/SUB[/...]
3610
3656
  */
3611
- static validateDepin(name) {
3612
- if (!name || typeof name !== 'string') {
3613
- throw new InvalidAssetNameError('DEPIN asset name must be a non-empty string', name);
3614
- }
3657
+ static validateDepin(name, network) {
3658
+ ensureName(name, 'DEPIN asset name');
3615
3659
 
3616
3660
  if (!name.startsWith(ASSET_NAME_RULES.DEPIN.prefix)) {
3617
3661
  throw new InvalidAssetNameError(
@@ -3620,58 +3664,41 @@ var NeuraiAssetsBundle = (function (exports) {
3620
3664
  );
3621
3665
  }
3622
3666
 
3623
- if (name.length > ASSET_NAME_RULES.DEPIN.maxLength) {
3624
- throw new InvalidAssetNameError(
3625
- `DEPIN asset name cannot exceed ${ASSET_NAME_RULES.DEPIN.maxLength} characters`,
3626
- name
3627
- );
3667
+ if (network && !isTestnet(network)) {
3668
+ throw new InvalidAssetNameError('DEPIN assets are only available in testnet', name);
3628
3669
  }
3629
3670
 
3630
- const parts = name.split(ASSET_NAME_RULES.DEPIN.separator);
3631
- if (parts.length === 0) {
3632
- throw new InvalidAssetNameError('DEPIN asset name is invalid', name);
3671
+ ensureMaxLength(name, getMaxAssetNameLength(network || 'xna-test'), 'DEPIN asset name');
3672
+
3673
+ if (name !== name.toUpperCase()) {
3674
+ throw new InvalidAssetNameError('DEPIN asset name must be uppercase', name);
3633
3675
  }
3634
3676
 
3635
- const rootPart = parts[0].substring(1);
3636
- if (rootPart.length < ASSET_NAME_RULES.DEPIN.minLength) {
3677
+ if (!isDepinIndicator(name)) {
3637
3678
  throw new InvalidAssetNameError(
3638
- `DEPIN root name must be at least ${ASSET_NAME_RULES.DEPIN.minLength} characters`,
3679
+ 'DEPIN asset name can only contain A-Z, 0-9, underscore, period, and separator /',
3639
3680
  name
3640
3681
  );
3641
3682
  }
3642
3683
 
3643
- if (rootPart !== rootPart.toUpperCase()) {
3644
- throw new InvalidAssetNameError('DEPIN asset name must be uppercase', name);
3645
- }
3684
+ const parts = name.split(ASSET_NAME_RULES.DEPIN.separator);
3646
3685
 
3647
- if (!ASSET_NAME_RULES.DEPIN.pattern.test(rootPart)) {
3686
+ if (parts.length > 1) {
3687
+ parts.forEach(part => {
3688
+ if (part.length < MIN_ASSET_LENGTH) {
3689
+ throw new InvalidAssetNameError(
3690
+ `Each DEPIN sub-part must be at least ${MIN_ASSET_LENGTH} characters`,
3691
+ name
3692
+ );
3693
+ }
3694
+ });
3695
+ } else if (name.length < MIN_ASSET_LENGTH + 1) {
3648
3696
  throw new InvalidAssetNameError(
3649
- 'DEPIN asset name can only contain A-Z, 0-9, underscore, and period',
3697
+ `DEPIN name must be at least ${MIN_ASSET_LENGTH} characters (excluding &)`,
3650
3698
  name
3651
3699
  );
3652
3700
  }
3653
3701
 
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
3702
  return true;
3676
3703
  }
3677
3704
 
@@ -3679,24 +3706,31 @@ var NeuraiAssetsBundle = (function (exports) {
3679
3706
  * Validate owner token name
3680
3707
  * Format: ASSETNAME!
3681
3708
  */
3682
- static validateOwnerToken(name) {
3683
- if (!name || typeof name !== 'string') {
3684
- throw new InvalidAssetNameError('Owner token name must be a non-empty string', name);
3685
- }
3709
+ static validateOwnerToken(name, network) {
3710
+ ensureName(name, 'Owner token name');
3711
+
3712
+ ensureMaxLength(name, getMaxAssetNameLength(network), 'Owner token name');
3686
3713
 
3687
3714
  if (!name.endsWith('!')) {
3688
3715
  throw new InvalidAssetNameError('Owner token must end with !', name);
3689
3716
  }
3690
3717
 
3691
3718
  const assetName = name.substring(0, name.length - 1);
3719
+ const validBaseName = isNameValidBeforeTag(assetName)
3720
+ || (assetName.startsWith('&') && (() => {
3721
+ try {
3722
+ this.validateDepin(assetName, network);
3723
+ return true;
3724
+ } catch (error) {
3725
+ return false;
3726
+ }
3727
+ })());
3692
3728
 
3693
- // Validate the asset name part (could be ROOT, RESTRICTED, or DEPIN)
3694
- if (assetName.startsWith('$')) {
3695
- this.validateRestricted(assetName);
3696
- } else if (assetName.startsWith('&')) {
3697
- this.validateDepin(assetName);
3698
- } else {
3699
- this.validateRoot(assetName);
3729
+ if (!validBaseName) {
3730
+ throw new InvalidAssetNameError(
3731
+ 'Owner name contains invalid characters (Valid characters are: A-Z 0-9 _ .) (special characters can\'t be the first or last characters)',
3732
+ name
3733
+ );
3700
3734
  }
3701
3735
 
3702
3736
  return true;
@@ -3707,27 +3741,27 @@ var NeuraiAssetsBundle = (function (exports) {
3707
3741
  * @param {string} name - Asset name
3708
3742
  * @returns {string} Asset type ('ROOT', 'SUB', 'UNIQUE', 'QUALIFIER', 'RESTRICTED', 'DEPIN', 'OWNER')
3709
3743
  */
3710
- static validateAndDetectType(name) {
3744
+ static validateAndDetectType(name, network) {
3711
3745
  if (name.endsWith('!')) {
3712
- this.validateOwnerToken(name);
3746
+ this.validateOwnerToken(name, network);
3713
3747
  return 'OWNER';
3714
3748
  } else if (name.startsWith('#')) {
3715
- this.validateQualifier(name);
3749
+ this.validateQualifier(name, network);
3716
3750
  return name.includes('/') ? 'SUB_QUALIFIER' : 'QUALIFIER';
3717
3751
  } else if (name.startsWith('$')) {
3718
- this.validateRestricted(name);
3752
+ this.validateRestricted(name, network);
3719
3753
  return 'RESTRICTED';
3720
3754
  } else if (name.startsWith('&')) {
3721
- this.validateDepin(name);
3755
+ this.validateDepin(name, network);
3722
3756
  return 'DEPIN';
3723
3757
  } else if (name.includes('#')) {
3724
- this.validateUnique(name);
3758
+ this.validateUnique(name, network);
3725
3759
  return 'UNIQUE';
3726
3760
  } else if (name.includes('/')) {
3727
- this.validateSub(name);
3761
+ this.validateSub(name, network);
3728
3762
  return 'SUB';
3729
3763
  } else {
3730
- this.validateRoot(name);
3764
+ this.validateRoot(name, network);
3731
3765
  return 'ROOT';
3732
3766
  }
3733
3767
  }
@@ -4404,22 +4438,22 @@ var NeuraiAssetsBundle = (function (exports) {
4404
4438
 
4405
4439
  switch (type) {
4406
4440
  case 'ROOT':
4407
- AssetNameValidator.validateRoot(assetName);
4441
+ AssetNameValidator.validateRoot(assetName, this.network);
4408
4442
  break;
4409
4443
  case 'SUB':
4410
- AssetNameValidator.validateSub(assetName);
4444
+ AssetNameValidator.validateSub(assetName, this.network);
4411
4445
  break;
4412
4446
  case 'UNIQUE':
4413
- AssetNameValidator.validateUnique(assetName);
4447
+ AssetNameValidator.validateUnique(assetName, this.network);
4414
4448
  break;
4415
4449
  case 'QUALIFIER':
4416
- AssetNameValidator.validateQualifier(assetName);
4450
+ AssetNameValidator.validateQualifier(assetName, this.network);
4417
4451
  break;
4418
4452
  case 'RESTRICTED':
4419
- AssetNameValidator.validateRestricted(assetName);
4453
+ AssetNameValidator.validateRestricted(assetName, this.network);
4420
4454
  break;
4421
4455
  case 'DEPIN':
4422
- AssetNameValidator.validateDepin(assetName);
4456
+ AssetNameValidator.validateDepin(assetName, this.network);
4423
4457
  break;
4424
4458
  default:
4425
4459
  throw new Error(`Unknown asset type: ${type}`);
@@ -5840,7 +5874,7 @@ var NeuraiAssetsBundle = (function (exports) {
5840
5874
  *
5841
5875
  * QUALIFIER assets:
5842
5876
  * - KYC/compliance tags (e.g., #KYC_VERIFIED, #ACCREDITED)
5843
- * - Format: #NAME or #ROOT/SUB
5877
+ * - Format: #NAME or #ROOT/#SUB
5844
5878
  * - Cost: 2000 XNA (root) or 200 XNA (sub-qualifier)
5845
5879
  * - Quantity: 1-10 units only
5846
5880
  * - Units: Always 0 (non-divisible)
@@ -7305,7 +7339,7 @@ var NeuraiAssetsBundle = (function (exports) {
7305
7339
  /**
7306
7340
  * Create a ROOT asset
7307
7341
  * @param {object} params - Asset creation parameters
7308
- * @param {string} params.assetName - Asset name (3-30 chars, A-Z 0-9 _ .)
7342
+ * @param {string} params.assetName - Asset name (3-31 visible chars on mainnet, A-Z 0-9 _ .)
7309
7343
  * @param {number} params.quantity - Total supply
7310
7344
  * @param {number} [params.units=0] - Decimal places (0-8)
7311
7345
  * @param {boolean} [params.reissuable=true] - Can mint more later