@neuraiproject/neurai-assets 1.3.3 → 1.4.1

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 CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  Complete asset management library for Neurai blockchain. Supports creation, reissuance, and queries for all asset types in a non-custodial way.
4
4
 
5
+ > **1.4.1**: fix — the constructor dropped `config.assetMarker`, so the
6
+ > wallet-level override documented in 1.4.0 never reached the builders
7
+ > (per-operation `params.assetMarker` was unaffected). Precedence is now
8
+ > effective: `params.assetMarker` > `config.assetMarker` > node.
9
+ >
10
+ > **1.4.0**: NIP-040 `assetMarker` in `localRawBuild` (see below); RPC
11
+ > rejection messages from `@neuraiproject/neurai-rpc` >= 0.5 are surfaced
12
+ > correctly (they carry no `.message`); name-length caps now mirror the node
13
+ > (full name, owner `!` included: 31 mainnet / 121 testnet-regtest, validated
14
+ > against a regtest node); peer `neurai-rpc ^0.6.0`.
15
+
5
16
  ## Features
6
17
 
7
18
  - ✅ **Non-custodial**: Library builds unsigned transactions, your wallet signs them
@@ -525,6 +536,26 @@ The library automatically validates that the owner token is returned in each ope
525
536
 
526
537
  **Note**: In addition to the burned cost, all operations pay a network fee (calculated automatically).
527
538
 
539
+ ## NIP-040 asset marker (local raw builds)
540
+
541
+ Asset payloads open with a 3-byte marker that NIP-040 migrates from the
542
+ Ravencoin-inherited `rvn` to `xna` at an activation height per network
543
+ (testnet: 303000, already crossed; regtest: 1; mainnet: not scheduled yet).
544
+
545
+ - Transactions built **through the node** (`createrawtransaction`) need
546
+ nothing: the node stamps the marker itself.
547
+ - The `localRawBuild` metadata (consumed by
548
+ `@neuraiproject/neurai-create-transaction createFromOperation`, >= 0.7.0)
549
+ now carries `params.assetMarker`. Builders resolve it once per build:
550
+ 1. `params.assetMarker` / `config.assetMarker` if you set it (`'rvn'` |
551
+ `'xna'` — offline builds or tests);
552
+ 2. otherwise the node's `getblockchaininfo.asset_marker` (node commit
553
+ `347362b` or later);
554
+ 3. `'rvn'` when the node predates that field or the call fails — which
555
+ matches what such a node enforces.
556
+
557
+ No height tables and no network inference: the node (or you) decides.
558
+
528
559
  ## Validations
529
560
 
530
561
  The library validates client-side:
@@ -7,6 +7,55 @@ var NeuraiAssetsBundle = (function (exports) {
7
7
 
8
8
  var src = {exports: {}};
9
9
 
10
+ /**
11
+ * Extract a human-readable message from an RPC rejection.
12
+ *
13
+ * @neuraiproject/neurai-rpc >= 0.5 never rejects with a plain Error, so
14
+ * `error.message` is undefined for every node failure. It uses three shapes:
15
+ *
16
+ * 1. {error: {code, message}, description} JSON-RPC error (also on HTTP 200)
17
+ * 2. {statusText, status, description, error} HTTP response other than 200
18
+ * 3. {originalError, type: 'ServerUnreachable', error, description}
19
+ *
20
+ * In shape 3 `error` is a string, in shapes 1 and 2 it is an object (or
21
+ * null). Plain Errors (thrown by this library or by mocks) keep working
22
+ * through the `error.message` candidate.
23
+ */
24
+
25
+ var rpcErrorMessage_1;
26
+ var hasRequiredRpcErrorMessage;
27
+
28
+ function requireRpcErrorMessage () {
29
+ if (hasRequiredRpcErrorMessage) return rpcErrorMessage_1;
30
+ hasRequiredRpcErrorMessage = 1;
31
+ function rpcErrorMessage(error) {
32
+ if (!error) {
33
+ return '';
34
+ }
35
+ if (typeof error === 'string') {
36
+ return error;
37
+ }
38
+
39
+ const candidates = [
40
+ error.error && error.error.message,
41
+ typeof error.error === 'string' ? error.error : null,
42
+ error.description,
43
+ error.message,
44
+ error.statusText
45
+ ];
46
+
47
+ for (const candidate of candidates) {
48
+ if (typeof candidate === 'string' && candidate.length > 0) {
49
+ return candidate;
50
+ }
51
+ }
52
+ return '';
53
+ }
54
+
55
+ rpcErrorMessage_1 = { rpcErrorMessage };
56
+ return rpcErrorMessage_1;
57
+ }
58
+
10
59
  /**
11
60
  * Validation Error Classes
12
61
  * Errors thrown during parameter validation
@@ -332,6 +381,7 @@ var NeuraiAssetsBundle = (function (exports) {
332
381
  function requireAssetQueries () {
333
382
  if (hasRequiredAssetQueries) return AssetQueries_1;
334
383
  hasRequiredAssetQueries = 1;
384
+ const { rpcErrorMessage } = requireRpcErrorMessage();
335
385
  const { AssetNotFoundError, InvalidAddressError } = requireErrors();
336
386
 
337
387
  class AssetQueries {
@@ -373,14 +423,14 @@ var NeuraiAssetsBundle = (function (exports) {
373
423
  }
374
424
 
375
425
  // RPC error - likely asset doesn't exist
376
- if (error.message && error.message.includes('not found')) {
426
+ if (rpcErrorMessage(error).includes('not found')) {
377
427
  throw new AssetNotFoundError(
378
428
  `Asset ${assetName} not found on blockchain`,
379
429
  assetName
380
430
  );
381
431
  }
382
432
 
383
- throw new Error(`Failed to get asset data: ${error.message}`);
433
+ throw new Error(`Failed to get asset data: ${rpcErrorMessage(error)}`);
384
434
  }
385
435
  }
386
436
 
@@ -397,7 +447,7 @@ var NeuraiAssetsBundle = (function (exports) {
397
447
  const assets = await this.rpc('listassets', [filter, verbose, count, start]);
398
448
  return assets || [];
399
449
  } catch (error) {
400
- throw new Error(`Failed to list assets: ${error.message}`);
450
+ throw new Error(`Failed to list assets: ${rpcErrorMessage(error)}`);
401
451
  }
402
452
  }
403
453
 
@@ -415,7 +465,7 @@ var NeuraiAssetsBundle = (function (exports) {
415
465
  const myAssets = await this.rpc('listmyassets', [assetName, verbose, count, start, confs]);
416
466
  return myAssets || {};
417
467
  } catch (error) {
418
- throw new Error(`Failed to list my assets: ${error.message}`);
468
+ throw new Error(`Failed to list my assets: ${rpcErrorMessage(error)}`);
419
469
  }
420
470
  }
421
471
 
@@ -436,13 +486,13 @@ var NeuraiAssetsBundle = (function (exports) {
436
486
  const result = await this.rpc('listaddressesbyasset', [assetName, onlyCount, count, start]);
437
487
  return result || (onlyCount ? 0 : []);
438
488
  } catch (error) {
439
- if (error.message && error.message.includes('not found')) {
489
+ if (rpcErrorMessage(error).includes('not found')) {
440
490
  throw new AssetNotFoundError(
441
491
  `Asset ${assetName} not found on blockchain`,
442
492
  assetName
443
493
  );
444
494
  }
445
- throw new Error(`Failed to list addresses by asset: ${error.message}`);
495
+ throw new Error(`Failed to list addresses by asset: ${rpcErrorMessage(error)}`);
446
496
  }
447
497
  }
448
498
 
@@ -463,7 +513,7 @@ var NeuraiAssetsBundle = (function (exports) {
463
513
  const result = await this.rpc('listassetbalancesbyaddress', [address, onlyTotal, count, start]);
464
514
  return result || (onlyTotal ? 0 : []);
465
515
  } catch (error) {
466
- throw new Error(`Failed to list asset balances by address: ${error.message}`);
516
+ throw new Error(`Failed to list asset balances by address: ${rpcErrorMessage(error)}`);
467
517
  }
468
518
  }
469
519
 
@@ -487,10 +537,10 @@ var NeuraiAssetsBundle = (function (exports) {
487
537
  return result === true || result === 1;
488
538
  } catch (error) {
489
539
  // If tag doesn't exist or address doesn't have it, return false
490
- if (error.message && (error.message.includes('not found') || error.message.includes('does not have'))) {
540
+ if (rpcErrorMessage(error).includes('not found') || rpcErrorMessage(error).includes('does not have')) {
491
541
  return false;
492
542
  }
493
- throw new Error(`Failed to check address tag: ${error.message}`);
543
+ throw new Error(`Failed to check address tag: ${rpcErrorMessage(error)}`);
494
544
  }
495
545
  }
496
546
 
@@ -509,10 +559,10 @@ var NeuraiAssetsBundle = (function (exports) {
509
559
  return tags || [];
510
560
  } catch (error) {
511
561
  // If no tags found, return empty array
512
- if (error.message && error.message.includes('not found')) {
562
+ if (rpcErrorMessage(error).includes('not found')) {
513
563
  return [];
514
564
  }
515
- throw new Error(`Failed to list tags for address: ${error.message}`);
565
+ throw new Error(`Failed to list tags for address: ${rpcErrorMessage(error)}`);
516
566
  }
517
567
  }
518
568
 
@@ -530,13 +580,13 @@ var NeuraiAssetsBundle = (function (exports) {
530
580
  const addresses = await this.rpc('listaddressesfortag', [qualifierName]);
531
581
  return addresses || [];
532
582
  } catch (error) {
533
- if (error.message && error.message.includes('not found')) {
583
+ if (rpcErrorMessage(error).includes('not found')) {
534
584
  throw new AssetNotFoundError(
535
585
  `Qualifier ${qualifierName} not found on blockchain`,
536
586
  qualifierName
537
587
  );
538
588
  }
539
- throw new Error(`Failed to list addresses for tag: ${error.message}`);
589
+ throw new Error(`Failed to list addresses for tag: ${rpcErrorMessage(error)}`);
540
590
  }
541
591
  }
542
592
 
@@ -560,10 +610,10 @@ var NeuraiAssetsBundle = (function (exports) {
560
610
  return result === true || result === 1;
561
611
  } catch (error) {
562
612
  // If address doesn't meet requirements, return false
563
- if (error.message && (error.message.includes('not found') || error.message.includes('does not meet'))) {
613
+ if (rpcErrorMessage(error).includes('not found') || rpcErrorMessage(error).includes('does not meet')) {
564
614
  return false;
565
615
  }
566
- throw new Error(`Failed to check address restriction: ${error.message}`);
616
+ throw new Error(`Failed to check address restriction: ${rpcErrorMessage(error)}`);
567
617
  }
568
618
  }
569
619
 
@@ -590,7 +640,7 @@ var NeuraiAssetsBundle = (function (exports) {
590
640
  }
591
641
  return false;
592
642
  } catch (error) {
593
- throw new Error(`Failed to check if address is frozen: ${error.message}`);
643
+ throw new Error(`Failed to check if address is frozen: ${rpcErrorMessage(error)}`);
594
644
  }
595
645
  }
596
646
 
@@ -608,13 +658,13 @@ var NeuraiAssetsBundle = (function (exports) {
608
658
  const result = await this.rpc('checkglobalrestriction', [restrictedAssetName]);
609
659
  return result === true || result === 1;
610
660
  } catch (error) {
611
- if (error.message && error.message.includes('not found')) {
661
+ if (rpcErrorMessage(error).includes('not found')) {
612
662
  throw new AssetNotFoundError(
613
663
  `Restricted asset ${restrictedAssetName} not found on blockchain`,
614
664
  restrictedAssetName
615
665
  );
616
666
  }
617
- throw new Error(`Failed to check global restriction: ${error.message}`);
667
+ throw new Error(`Failed to check global restriction: ${rpcErrorMessage(error)}`);
618
668
  }
619
669
  }
620
670
 
@@ -632,13 +682,13 @@ var NeuraiAssetsBundle = (function (exports) {
632
682
  const result = await this.rpc('getverifierstring', [restrictedAssetName]);
633
683
  return result || '';
634
684
  } catch (error) {
635
- if (error.message && error.message.includes('not found')) {
685
+ if (rpcErrorMessage(error).includes('not found')) {
636
686
  throw new AssetNotFoundError(
637
687
  `Restricted asset ${restrictedAssetName} not found on blockchain`,
638
688
  restrictedAssetName
639
689
  );
640
690
  }
641
- throw new Error(`Failed to get verifier string: ${error.message}`);
691
+ throw new Error(`Failed to get verifier string: ${rpcErrorMessage(error)}`);
642
692
  }
643
693
  }
644
694
 
@@ -680,7 +730,7 @@ var NeuraiAssetsBundle = (function (exports) {
680
730
  const result = await this.rpc('getsnapshotrequest', [assetName, blockHeight]);
681
731
  return result;
682
732
  } catch (error) {
683
- throw new Error(`Failed to get snapshot request: ${error.message}`);
733
+ throw new Error(`Failed to get snapshot request: ${rpcErrorMessage(error)}`);
684
734
  }
685
735
  }
686
736
 
@@ -703,7 +753,7 @@ var NeuraiAssetsBundle = (function (exports) {
703
753
  const result = await this.rpc('cancelsnapshotrequest', [assetName, blockHeight]);
704
754
  return result === true || result === 1;
705
755
  } catch (error) {
706
- throw new Error(`Failed to cancel snapshot request: ${error.message}`);
756
+ throw new Error(`Failed to cancel snapshot request: ${rpcErrorMessage(error)}`);
707
757
  }
708
758
  }
709
759
 
@@ -721,13 +771,13 @@ var NeuraiAssetsBundle = (function (exports) {
721
771
  const result = await this.rpc('listdepinholders', [assetName]);
722
772
  return result || [];
723
773
  } catch (error) {
724
- if (error.message && error.message.includes('not found')) {
774
+ if (rpcErrorMessage(error).includes('not found')) {
725
775
  throw new AssetNotFoundError(
726
776
  `DEPIN asset ${assetName} not found on blockchain`,
727
777
  assetName
728
778
  );
729
779
  }
730
- throw new Error(`Failed to list DEPIN holders: ${error.message}`);
780
+ throw new Error(`Failed to list DEPIN holders: ${rpcErrorMessage(error)}`);
731
781
  }
732
782
  }
733
783
 
@@ -750,7 +800,7 @@ var NeuraiAssetsBundle = (function (exports) {
750
800
  const result = await this.rpc('checkdepinvalidity', [assetName, address]);
751
801
  return result || { has_asset: false };
752
802
  } catch (error) {
753
- throw new Error(`Failed to check DEPIN validity: ${error.message}`);
803
+ throw new Error(`Failed to check DEPIN validity: ${rpcErrorMessage(error)}`);
754
804
  }
755
805
  }
756
806
 
@@ -764,7 +814,7 @@ var NeuraiAssetsBundle = (function (exports) {
764
814
  const assets = await this.listAssets('*', false, 1, 0);
765
815
  return Array.isArray(assets) ? assets.length : 0;
766
816
  } catch (error) {
767
- throw new Error(`Failed to get asset count: ${error.message}`);
817
+ throw new Error(`Failed to get asset count: ${rpcErrorMessage(error)}`);
768
818
  }
769
819
  }
770
820
 
@@ -1050,7 +1100,7 @@ var NeuraiAssetsBundle = (function (exports) {
1050
1100
  addressPrefix: 'N',
1051
1101
  authScriptAddressPrefix: 'nq1',
1052
1102
  pqAddressPrefix: 'nq1',
1053
- assetNameMaxLength: 32,
1103
+ assetNameMaxLength: 31,
1054
1104
  defaultRPCPort: 19001,
1055
1105
  coin: 'XNA',
1056
1106
  baseNetwork: 'xna'
@@ -1061,7 +1111,7 @@ var NeuraiAssetsBundle = (function (exports) {
1061
1111
  addressPrefix: 't',
1062
1112
  authScriptAddressPrefix: 'tnq1',
1063
1113
  pqAddressPrefix: 'tnq1',
1064
- assetNameMaxLength: 32, // Same as mainnet
1114
+ assetNameMaxLength: 121, // DePIN networks (testnet/regtest) extend the cap
1065
1115
  defaultRPCPort: 19101,
1066
1116
  coin: 'TXNA',
1067
1117
  baseNetwork: 'xna-test'
@@ -1072,7 +1122,7 @@ var NeuraiAssetsBundle = (function (exports) {
1072
1122
  addressPrefix: 'N',
1073
1123
  authScriptAddressPrefix: 'nq1',
1074
1124
  pqAddressPrefix: 'nq1',
1075
- assetNameMaxLength: 32,
1125
+ assetNameMaxLength: 31,
1076
1126
  defaultRPCPort: 19001,
1077
1127
  coin: 'XNA',
1078
1128
  baseNetwork: 'xna'
@@ -1083,7 +1133,7 @@ var NeuraiAssetsBundle = (function (exports) {
1083
1133
  addressPrefix: 't',
1084
1134
  authScriptAddressPrefix: 'tnq1',
1085
1135
  pqAddressPrefix: 'tnq1',
1086
- assetNameMaxLength: 32,
1136
+ assetNameMaxLength: 121,
1087
1137
  defaultRPCPort: 19101,
1088
1138
  coin: 'TXNA',
1089
1139
  baseNetwork: 'xna-test'
@@ -1911,6 +1961,8 @@ var NeuraiAssetsBundle = (function (exports) {
1911
1961
  function requireNetworkDetector () {
1912
1962
  if (hasRequiredNetworkDetector) return networkDetector;
1913
1963
  hasRequiredNetworkDetector = 1;
1964
+ const { rpcErrorMessage } = requireRpcErrorMessage();
1965
+
1914
1966
  const {
1915
1967
  NETWORKS,
1916
1968
  areAddressNetworksCompatible,
@@ -1945,7 +1997,7 @@ var NeuraiAssetsBundle = (function (exports) {
1945
1997
  // Default to mainnet
1946
1998
  return 'xna';
1947
1999
  } catch (error) {
1948
- throw new Error(`Failed to detect network from RPC: ${error.message}`);
2000
+ throw new Error(`Failed to detect network from RPC: ${rpcErrorMessage(error)}`);
1949
2001
  }
1950
2002
  }
1951
2003
 
@@ -2465,6 +2517,7 @@ var NeuraiAssetsBundle = (function (exports) {
2465
2517
  function requireOwnerTokenManager () {
2466
2518
  if (hasRequiredOwnerTokenManager) return OwnerTokenManager_1;
2467
2519
  hasRequiredOwnerTokenManager = 1;
2520
+ const { rpcErrorMessage } = requireRpcErrorMessage();
2468
2521
  const { AssetNameParser } = requireUtils();
2469
2522
  const {
2470
2523
  OwnerTokenNotFoundError,
@@ -2523,7 +2576,7 @@ var NeuraiAssetsBundle = (function (exports) {
2523
2576
  }
2524
2577
 
2525
2578
  throw new AssetError(
2526
- `Failed to find owner token ${ownerTokenName}: ${error.message}`
2579
+ `Failed to find owner token ${ownerTokenName}: ${rpcErrorMessage(error)}`
2527
2580
  );
2528
2581
  }
2529
2582
  }
@@ -2661,7 +2714,7 @@ var NeuraiAssetsBundle = (function (exports) {
2661
2714
  return ownerTokenUTXOs;
2662
2715
  } catch (error) {
2663
2716
  throw new AssetError(
2664
- `Failed to get owner tokens: ${error.message}`
2717
+ `Failed to get owner tokens: ${rpcErrorMessage(error)}`
2665
2718
  );
2666
2719
  }
2667
2720
  }
@@ -2853,6 +2906,7 @@ var NeuraiAssetsBundle = (function (exports) {
2853
2906
  function requireUTXOSelector () {
2854
2907
  if (hasRequiredUTXOSelector) return UTXOSelector_1;
2855
2908
  hasRequiredUTXOSelector = 1;
2909
+ const { rpcErrorMessage } = requireRpcErrorMessage();
2856
2910
  const { InsufficientFundsError } = requireErrors();
2857
2911
  const { estimateTransactionVbytes } = requireFeeSizing();
2858
2912
 
@@ -2891,7 +2945,7 @@ var NeuraiAssetsBundle = (function (exports) {
2891
2945
  return utxos.filter(utxo => !utxo.assetName || utxo.assetName === 'XNA');
2892
2946
  }
2893
2947
  } catch (error) {
2894
- throw new Error(`Failed to get UTXOs: ${error.message}`);
2948
+ throw new Error(`Failed to get UTXOs: ${rpcErrorMessage(error)}`);
2895
2949
  }
2896
2950
  }
2897
2951
 
@@ -3493,7 +3547,10 @@ var NeuraiAssetsBundle = (function (exports) {
3493
3547
  const { InvalidAssetNameError } = requireErrors();
3494
3548
 
3495
3549
  const MIN_ASSET_LENGTH = 3;
3496
- const MAINNET_MAX_NAME_LENGTH = 32;
3550
+ // Full-name caps, mirror of the node (assets_fromscript.cpp:31-47): the limit
3551
+ // applies to the COMPLETE name, owner '!' and tags included, so a mainnet
3552
+ // root is effectively capped at 30 (its owner token "ROOT!" must fit in 31).
3553
+ const MAINNET_MAX_NAME_LENGTH = 31;
3497
3554
  const TESTNET_MAX_NAME_LENGTH = 121;
3498
3555
 
3499
3556
  const ROOT_NAME_CHARACTERS = /^[A-Z0-9._]{3,}$/;
@@ -4363,6 +4420,7 @@ var NeuraiAssetsBundle = (function (exports) {
4363
4420
  function requireBaseAssetTransactionBuilder () {
4364
4421
  if (hasRequiredBaseAssetTransactionBuilder) return BaseAssetTransactionBuilder_1;
4365
4422
  hasRequiredBaseAssetTransactionBuilder = 1;
4423
+ const { rpcErrorMessage } = requireRpcErrorMessage();
4366
4424
  const { BurnManager, OwnerTokenManager, UTXOSelector, OutputOrderer } = requireManagers();
4367
4425
  const { AssetNameValidator, AmountValidator } = requireValidators();
4368
4426
 
@@ -4423,6 +4481,9 @@ var NeuraiAssetsBundle = (function (exports) {
4423
4481
  // post-selection recompute). The fee rate is stable for the duration of
4424
4482
  // a single build, so cache the first lookup and reuse it.
4425
4483
  this._feeRatePromise = null;
4484
+
4485
+ // NIP-040 marker for the localRawBuild metadata; resolved once per build.
4486
+ this._assetMarkerPromise = null;
4426
4487
  }
4427
4488
 
4428
4489
  /**
@@ -4515,7 +4576,7 @@ var NeuraiAssetsBundle = (function (exports) {
4515
4576
 
4516
4577
  return rawTx;
4517
4578
  } catch (error) {
4518
- throw new Error(`Failed to create raw transaction: ${error.message}`);
4579
+ throw new Error(`Failed to create raw transaction: ${rpcErrorMessage(error)}`);
4519
4580
  }
4520
4581
  }
4521
4582
 
@@ -4677,19 +4738,73 @@ var NeuraiAssetsBundle = (function (exports) {
4677
4738
  return Math.round(amount * 100000000);
4678
4739
  }
4679
4740
 
4741
+ /**
4742
+ * NIP-040 marker for every asset output of the localRawBuild metadata.
4743
+ *
4744
+ * The chain decides which marker ("rvn" or "xna") new asset outputs must
4745
+ * carry, per network and height, and the node reports the one required for
4746
+ * the next block as `getblockchaininfo.asset_marker` (node commit 347362b).
4747
+ * Resolution order:
4748
+ * 1. `params.assetMarker` (explicit caller override — offline builds,
4749
+ * tests, or a node this library should not ask);
4750
+ * 2. `getblockchaininfo.asset_marker` from the connected node;
4751
+ * 3. `'rvn'` when the node predates the field or the call fails
4752
+ * (matches what such a node enforces; documented in the README).
4753
+ * The RPC-built transaction path never needs this: the node stamps the
4754
+ * marker itself in `createrawtransaction`.
4755
+ *
4756
+ * @returns {Promise<'rvn'|'xna'>} Marker for locally built asset outputs
4757
+ */
4758
+ resolveAssetMarker() {
4759
+ if (!this._assetMarkerPromise) {
4760
+ this._assetMarkerPromise = this._fetchAssetMarker();
4761
+ }
4762
+ return this._assetMarkerPromise;
4763
+ }
4764
+
4765
+ async _fetchAssetMarker() {
4766
+ const override = this.params.assetMarker;
4767
+ if (override !== undefined && override !== null) {
4768
+ if (override !== 'rvn' && override !== 'xna') {
4769
+ throw new Error(
4770
+ `Invalid assetMarker: ${override} (expected 'rvn' or 'xna', the value of getblockchaininfo.asset_marker)`
4771
+ );
4772
+ }
4773
+ return override;
4774
+ }
4775
+
4776
+ let info = null;
4777
+ try {
4778
+ info = await this.rpc('getblockchaininfo', []);
4779
+ } catch (error) {
4780
+ return 'rvn';
4781
+ }
4782
+ const marker = info ? info.asset_marker : undefined;
4783
+ if (marker === undefined || marker === null) {
4784
+ return 'rvn';
4785
+ }
4786
+ if (marker !== 'rvn' && marker !== 'xna') {
4787
+ throw new Error(`Node reported an unknown asset_marker: ${marker}`);
4788
+ }
4789
+ return marker;
4790
+ }
4791
+
4680
4792
  /**
4681
4793
  * Build a typed local raw build payload compatible with
4682
4794
  * @neuraiproject/neurai-create-transaction createFromOperation(...)
4683
4795
  *
4796
+ * Stamps the NIP-040 `assetMarker` (see resolveAssetMarker) so
4797
+ * createFromOperation >= 0.7.0 emits the marker the chain requires.
4798
+ *
4684
4799
  * @param {string} operationType - Operation type
4685
4800
  * @param {Array} inputs - Builder inputs
4686
4801
  * @param {object|null} burnInfo - Burn metadata
4687
4802
  * @param {string|null} changeAddress - XNA change address
4688
4803
  * @param {number|null} changeAmount - XNA change amount in XNA
4689
4804
  * @param {object} operationParams - Operation-specific params
4690
- * @returns {{ operationType: string, params: object }} Local raw build
4805
+ * @returns {Promise<{ operationType: string, params: object }>} Local raw build
4691
4806
  */
4692
- buildLocalRawBuild(
4807
+ async buildLocalRawBuild(
4693
4808
  operationType,
4694
4809
  inputs,
4695
4810
  burnInfo = null,
@@ -4699,6 +4814,7 @@ var NeuraiAssetsBundle = (function (exports) {
4699
4814
  ) {
4700
4815
  const params = {
4701
4816
  inputs: this.toRawTxInputs(inputs),
4817
+ assetMarker: await this.resolveAssetMarker(),
4702
4818
  ...operationParams
4703
4819
  };
4704
4820
 
@@ -4835,7 +4951,7 @@ var NeuraiAssetsBundle = (function (exports) {
4835
4951
  return assetData !== null && assetData !== undefined;
4836
4952
  } catch (error) {
4837
4953
  // If asset doesn't exist, RPC will throw error
4838
- if (error.message && error.message.includes('not found')) {
4954
+ if (rpcErrorMessage(error).includes('not found')) {
4839
4955
  return false;
4840
4956
  }
4841
4957
  // Re-throw other errors
@@ -4852,7 +4968,7 @@ var NeuraiAssetsBundle = (function (exports) {
4852
4968
  try {
4853
4969
  return await this.rpc('getassetdata', [assetName]);
4854
4970
  } catch (error) {
4855
- if (error.message && error.message.includes('not found')) {
4971
+ if (rpcErrorMessage(error).includes('not found')) {
4856
4972
  return null;
4857
4973
  }
4858
4974
  throw error;
@@ -5038,7 +5154,7 @@ var NeuraiAssetsBundle = (function (exports) {
5038
5154
  assetName,
5039
5155
  ownerTokenName: assetName + '!',
5040
5156
  operationType: 'ISSUE_ROOT',
5041
- localRawBuild: this.buildLocalRawBuild(
5157
+ localRawBuild: await this.buildLocalRawBuild(
5042
5158
  'ISSUE_ROOT',
5043
5159
  inputs,
5044
5160
  burnInfo,
@@ -5306,7 +5422,7 @@ var NeuraiAssetsBundle = (function (exports) {
5306
5422
  ownerTokenName: assetName + '!',
5307
5423
  parentOwnerTokenUsed: ownerTokenName,
5308
5424
  operationType: 'ISSUE_SUB',
5309
- localRawBuild: this.buildLocalRawBuild(
5425
+ localRawBuild: await this.buildLocalRawBuild(
5310
5426
  'ISSUE_SUB',
5311
5427
  inputs,
5312
5428
  burnInfo,
@@ -5476,7 +5592,7 @@ var NeuraiAssetsBundle = (function (exports) {
5476
5592
  assetName,
5477
5593
  ownerTokenName: `${assetName}!`,
5478
5594
  operationType: 'ISSUE_DEPIN',
5479
- localRawBuild: this.buildLocalRawBuild(
5595
+ localRawBuild: await this.buildLocalRawBuild(
5480
5596
  'ISSUE_DEPIN',
5481
5597
  inputs,
5482
5598
  burnInfo,
@@ -5744,7 +5860,7 @@ var NeuraiAssetsBundle = (function (exports) {
5744
5860
  previousSupply: currentSupply,
5745
5861
  reissuableLocked: reissuable === false,
5746
5862
  operationType: 'REISSUE',
5747
- localRawBuild: this.buildLocalRawBuild(
5863
+ localRawBuild: await this.buildLocalRawBuild(
5748
5864
  'REISSUE',
5749
5865
  inputs,
5750
5866
  burnInfo,
@@ -6007,7 +6123,7 @@ var NeuraiAssetsBundle = (function (exports) {
6007
6123
  isDepin,
6008
6124
  ownerTokenUsed: isDepin ? ownerTokenName : null,
6009
6125
  operationType: 'TRANSFER',
6010
- localRawBuild: this.buildLocalRawBuild(
6126
+ localRawBuild: await this.buildLocalRawBuild(
6011
6127
  'TRANSFER',
6012
6128
  inputs,
6013
6129
  null, // no burn
@@ -6291,7 +6407,7 @@ var NeuraiAssetsBundle = (function (exports) {
6291
6407
  nftCount,
6292
6408
  ownerTokenUsed: ownerTokenName,
6293
6409
  operationType: 'ISSUE_UNIQUE',
6294
- localRawBuild: this.buildLocalRawBuild(
6410
+ localRawBuild: await this.buildLocalRawBuild(
6295
6411
  'ISSUE_UNIQUE',
6296
6412
  inputs,
6297
6413
  burnInfo,
@@ -6552,7 +6668,7 @@ var NeuraiAssetsBundle = (function (exports) {
6552
6668
  parentQualifier: isSub ? parsed.parent : null,
6553
6669
  parentQualifierUsed: parentQualifierName,
6554
6670
  operationType: isSub ? 'ISSUE_SUB_QUALIFIER' : 'ISSUE_QUALIFIER',
6555
- localRawBuild: this.buildLocalRawBuild(
6671
+ localRawBuild: await this.buildLocalRawBuild(
6556
6672
  isSub ? 'ISSUE_SUB_QUALIFIER' : 'ISSUE_QUALIFIER',
6557
6673
  inputs,
6558
6674
  burnInfo,
@@ -6806,7 +6922,7 @@ var NeuraiAssetsBundle = (function (exports) {
6806
6922
  verifierString,
6807
6923
  requiredQualifiers,
6808
6924
  operationType: 'ISSUE_RESTRICTED',
6809
- localRawBuild: this.buildLocalRawBuild(
6925
+ localRawBuild: await this.buildLocalRawBuild(
6810
6926
  'ISSUE_RESTRICTED',
6811
6927
  inputs,
6812
6928
  burnInfo,
@@ -7092,7 +7208,7 @@ var NeuraiAssetsBundle = (function (exports) {
7092
7208
  requiredQualifiers,
7093
7209
  reissuableLocked: reissuable === false,
7094
7210
  operationType: 'REISSUE_RESTRICTED',
7095
- localRawBuild: this.buildLocalRawBuild(
7211
+ localRawBuild: await this.buildLocalRawBuild(
7096
7212
  'REISSUE_RESTRICTED',
7097
7213
  inputs,
7098
7214
  burnInfo,
@@ -7334,7 +7450,7 @@ var NeuraiAssetsBundle = (function (exports) {
7334
7450
  targetAddresses,
7335
7451
  addressCount,
7336
7452
  operationType: isUntag ? 'UNTAG_ADDRESSES' : 'TAG_ADDRESSES',
7337
- localRawBuild: this.buildLocalRawBuild(
7453
+ localRawBuild: await this.buildLocalRawBuild(
7338
7454
  isUntag ? 'UNTAG_ADDRESSES' : 'TAG_ADDRESSES',
7339
7455
  inputs,
7340
7456
  burnInfo,
@@ -7596,7 +7712,7 @@ var NeuraiAssetsBundle = (function (exports) {
7596
7712
  targetAddresses: targetAddresses.length > 0 ? targetAddresses : null,
7597
7713
  addressCount: targetAddresses.length,
7598
7714
  operationType,
7599
- localRawBuild: this.buildLocalRawBuild(
7715
+ localRawBuild: await this.buildLocalRawBuild(
7600
7716
  operationType,
7601
7717
  inputs,
7602
7718
  null,
@@ -7757,6 +7873,10 @@ var NeuraiAssetsBundle = (function (exports) {
7757
7873
  * @param {Array<string>} config.addresses - Wallet addresses
7758
7874
  * @param {string} config.changeAddress - Default change address
7759
7875
  * @param {string} config.toAddress - Default receiving address
7876
+ * @param {('rvn'|'xna')} [config.assetMarker] - NIP-040 marker for locally
7877
+ * built raw transactions. Omit to use the node's
7878
+ * getblockchaininfo.asset_marker (falls back to 'rvn' on nodes that do
7879
+ * not report it). Per-operation params.assetMarker overrides this.
7760
7880
  */
7761
7881
  constructor(rpc, config = {}) {
7762
7882
  if (!rpc || typeof rpc !== 'function') {
@@ -7768,7 +7888,11 @@ var NeuraiAssetsBundle = (function (exports) {
7768
7888
  network: config.network || 'xna',
7769
7889
  addresses: config.addresses || [],
7770
7890
  changeAddress: config.changeAddress || null,
7771
- toAddress: config.toAddress || null
7891
+ toAddress: config.toAddress || null,
7892
+ // NIP-040: documented since 1.4.0 but dropped here until 1.4.1, which
7893
+ // made the config-level override silently inoperative (per-operation
7894
+ // params.assetMarker was unaffected).
7895
+ assetMarker: config.assetMarker
7772
7896
  };
7773
7897
 
7774
7898
  // Initialize query interface
@@ -7794,7 +7918,10 @@ var NeuraiAssetsBundle = (function (exports) {
7794
7918
  network: this.config.network,
7795
7919
  walletAddresses: this.config.addresses,
7796
7920
  changeAddress: params.changeAddress || this.config.changeAddress,
7797
- toAddress: params.toAddress || this.config.toAddress
7921
+ toAddress: params.toAddress || this.config.toAddress,
7922
+ // NIP-040: marker for the localRawBuild metadata. Undefined lets the
7923
+ // builder ask the node (getblockchaininfo.asset_marker).
7924
+ assetMarker: params.assetMarker !== undefined ? params.assetMarker : this.config.assetMarker
7798
7925
  };
7799
7926
  }
7800
7927