@neuraiproject/neurai-assets 1.2.1 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,7 +10,7 @@ Complete asset management library for Neurai blockchain. Supports creation, reis
10
10
  - ✅ **RPC queries**: Complete wrapper for all asset query methods
11
11
  - ✅ **Client-side validation**: Prevents errors before creating transactions
12
12
  - ✅ **Owner token protection**: Validation to prevent permanent loss
13
- - ✅ **PQ-ready networks**: Supports `xna-pq` and `xna-pq-test` with `nq1...` / `tnq1...` addresses
13
+ - ✅ **Legacy + AuthScript destinations**: Supports classic `N...` / `t...` and witness-v1 `nq1...` / `tnq1...` addresses
14
14
 
15
15
  ## Supported Asset Types
16
16
 
@@ -87,14 +87,14 @@ const signedTx = await wallet.signTransaction(result.rawTx);
87
87
  const txid = await wallet.broadcastTransaction(signedTx);
88
88
  ```
89
89
 
90
- You can also initialize the library with PQ networks and addresses:
90
+ You can also initialize the library with AuthScript addresses:
91
91
 
92
92
  ```javascript
93
93
  const assetsPQ = new NeuraiAssets(rpc, {
94
- network: 'xna-pq', // or 'xna-pq-test'
95
- addresses: ['nq1yourpqaddress...'],
96
- changeAddress: 'nq1yourpqchange...',
97
- toAddress: 'nq1recipientpqaddress...'
94
+ network: 'xna', // 'xna-pq' / 'xna-pq-test' remain valid aliases
95
+ addresses: ['nq1yourauthscriptaddress...'],
96
+ changeAddress: 'nq1yourauthscriptchange...',
97
+ toAddress: 'nq1recipientauthscriptaddress...'
98
98
  });
99
99
  ```
100
100
 
@@ -148,8 +148,8 @@ const result = await assets.createDepinAsset({
148
148
  });
149
149
  ```
150
150
 
151
- > **Note**: DEPIN assets always use `units = 0`. The library accepts both legacy
152
- > and PQ addresses as recipients depending on the configured network.
151
+ > **Note**: DEPIN assets always use `units = 0`. Recipient and change destinations
152
+ > can be either legacy or AuthScript, as long as they belong to the same chain family.
153
153
 
154
154
  ### Create UNIQUE Assets (NFTs)
155
155
 
@@ -504,17 +504,17 @@ const assets = new NeuraiAssets(rpc, {
504
504
  toAddress: 't...'
505
505
  });
506
506
 
507
- // PQ Mainnet
507
+ // AuthScript mainnet using the canonical network label
508
508
  const assetsPQ = new NeuraiAssets(rpc, {
509
- network: 'xna-pq',
509
+ network: 'xna',
510
510
  addresses: ['nq1...'],
511
511
  changeAddress: 'nq1...',
512
512
  toAddress: 'nq1...'
513
513
  });
514
514
 
515
- // PQ Testnet
515
+ // AuthScript testnet using the canonical network label
516
516
  const assetsPQTest = new NeuraiAssets(rpc, {
517
- network: 'xna-pq-test',
517
+ network: 'xna-test',
518
518
  addresses: ['tnq1...'],
519
519
  changeAddress: 'tnq1...',
520
520
  toAddress: 'tnq1...'
@@ -523,13 +523,13 @@ const assetsPQTest = new NeuraiAssets(rpc, {
523
523
 
524
524
  The library accepts these network names:
525
525
 
526
- - `xna`: legacy/mainnet address flow (`N...`)
527
- - `xna-test`: legacy/testnet address flow (`t...`)
528
- - `xna-pq`: PQ mainnet address flow (`nq1...`)
529
- - `xna-pq-test`: PQ testnet address flow (`tnq1...`)
526
+ - `xna`: mainnet chain family, valid for both legacy `N...` and AuthScript `nq1...`
527
+ - `xna-test`: testnet chain family, valid for both legacy `t...` and AuthScript `tnq1...`
528
+ - `xna-pq`: compatibility alias for AuthScript mainnet flows
529
+ - `xna-pq-test`: compatibility alias for AuthScript testnet flows
530
530
 
531
- If you need to derive PQ addresses, use `neurai-key` and pass the resulting `nq1...`
532
- or `tnq1...` addresses into this library.
531
+ If you need to derive AuthScript addresses, use `neurai-key` and pass the resulting
532
+ `nq1...` or `tnq1...` addresses into this library.
533
533
 
534
534
  ## Update Configuration
535
535
 
@@ -648,6 +648,6 @@ const txid = await wallet.broadcastTransaction(signedTx);
648
648
  console.log('Transaction ID:', txid);
649
649
  ```
650
650
 
651
- For PQ wallets, derive addresses externally with `neurai-key` using `xna-pq` or
652
- `xna-pq-test`, then initialize `NeuraiAssets` with those addresses and the matching
653
- network name.
651
+ For AuthScript wallets, derive addresses externally with `neurai-key`, then initialize
652
+ `NeuraiAssets` with those `nq1...` / `tnq1...` addresses. The recommended network labels
653
+ are `xna` and `xna-test`; `xna-pq` and `xna-pq-test` remain available as compatibility aliases.
@@ -1040,11 +1040,15 @@ var NeuraiAssetsBundle = (function (exports) {
1040
1040
  function requireNetworks () {
1041
1041
  if (hasRequiredNetworks) return networks;
1042
1042
  hasRequiredNetworks = 1;
1043
+ const MAINNET_NETWORKS = ['xna', 'mainnet', 'xna-pq', 'mainnet-pq'];
1044
+ const TESTNET_NETWORKS = ['xna-test', 'testnet', 'regtest', 'xna-pq-test', 'testnet-pq'];
1045
+
1043
1046
  const NETWORKS = {
1044
1047
  MAINNET: {
1045
1048
  name: 'xna',
1046
1049
  displayName: 'Neurai Mainnet',
1047
1050
  addressPrefix: 'N',
1051
+ authScriptAddressPrefix: 'nq1',
1048
1052
  pqAddressPrefix: 'nq1',
1049
1053
  assetNameMaxLength: 32,
1050
1054
  defaultRPCPort: 19001,
@@ -1055,6 +1059,7 @@ var NeuraiAssetsBundle = (function (exports) {
1055
1059
  name: 'xna-test',
1056
1060
  displayName: 'Neurai Testnet',
1057
1061
  addressPrefix: 't',
1062
+ authScriptAddressPrefix: 'tnq1',
1058
1063
  pqAddressPrefix: 'tnq1',
1059
1064
  assetNameMaxLength: 32, // Same as mainnet
1060
1065
  defaultRPCPort: 19101,
@@ -1063,8 +1068,9 @@ var NeuraiAssetsBundle = (function (exports) {
1063
1068
  },
1064
1069
  MAINNET_PQ: {
1065
1070
  name: 'xna-pq',
1066
- displayName: 'Neurai Mainnet PQ',
1071
+ displayName: 'Neurai Mainnet AuthScript',
1067
1072
  addressPrefix: 'N',
1073
+ authScriptAddressPrefix: 'nq1',
1068
1074
  pqAddressPrefix: 'nq1',
1069
1075
  assetNameMaxLength: 32,
1070
1076
  defaultRPCPort: 19001,
@@ -1073,8 +1079,9 @@ var NeuraiAssetsBundle = (function (exports) {
1073
1079
  },
1074
1080
  TESTNET_PQ: {
1075
1081
  name: 'xna-pq-test',
1076
- displayName: 'Neurai Testnet PQ',
1082
+ displayName: 'Neurai Testnet AuthScript',
1077
1083
  addressPrefix: 't',
1084
+ authScriptAddressPrefix: 'tnq1',
1078
1085
  pqAddressPrefix: 'tnq1',
1079
1086
  assetNameMaxLength: 32,
1080
1087
  defaultRPCPort: 19101,
@@ -1144,32 +1151,68 @@ var NeuraiAssetsBundle = (function (exports) {
1144
1151
 
1145
1152
  /**
1146
1153
  * Get network configuration
1154
+ * `xna-pq` / `xna-pq-test` are preserved as compatibility aliases for
1155
+ * AuthScript address flows on the same mainnet/testnet families.
1156
+ *
1147
1157
  * @param {string} networkName - Network name ('xna', 'xna-test', 'xna-pq', or 'xna-pq-test')
1148
1158
  * @returns {object} Network configuration
1149
1159
  */
1150
1160
  function getNetworkConfig(networkName) {
1151
- if (networkName === 'xna' || networkName === 'mainnet') {
1152
- return NETWORKS.MAINNET;
1153
- } else if (networkName === 'xna-test' || networkName === 'testnet') {
1154
- return NETWORKS.TESTNET;
1155
- } else if (networkName === 'xna-pq' || networkName === 'mainnet-pq') {
1156
- return NETWORKS.MAINNET_PQ;
1157
- } else if (networkName === 'xna-pq-test' || networkName === 'testnet-pq') {
1158
- return NETWORKS.TESTNET_PQ;
1161
+ if (MAINNET_NETWORKS.includes(networkName)) {
1162
+ return networkName === 'xna-pq' || networkName === 'mainnet-pq'
1163
+ ? NETWORKS.MAINNET_PQ
1164
+ : NETWORKS.MAINNET;
1165
+ } else if (TESTNET_NETWORKS.includes(networkName)) {
1166
+ return networkName === 'xna-pq-test' || networkName === 'testnet-pq'
1167
+ ? NETWORKS.TESTNET_PQ
1168
+ : NETWORKS.TESTNET;
1159
1169
  } else {
1160
1170
  throw new Error(`Unknown network: ${networkName}`);
1161
1171
  }
1162
1172
  }
1163
1173
 
1164
1174
  /**
1165
- * Detect network from address prefix
1175
+ * Resolve a network name to its chain family.
1176
+ * AuthScript aliases share the same family as legacy addresses.
1177
+ *
1178
+ * @param {string} networkName - Network name
1179
+ * @returns {'mainnet'|'testnet'} Network family
1180
+ */
1181
+ function resolveAddressNetworkFamily(networkName) {
1182
+ if (MAINNET_NETWORKS.includes(networkName)) {
1183
+ return 'mainnet';
1184
+ }
1185
+
1186
+ if (TESTNET_NETWORKS.includes(networkName)) {
1187
+ return 'testnet';
1188
+ }
1189
+
1190
+ throw new Error(`Unknown network: ${networkName}`);
1191
+ }
1192
+
1193
+ /**
1194
+ * Determine whether two network labels are compatible for address use.
1195
+ * This treats legacy and AuthScript labels on the same chain as compatible.
1196
+ *
1197
+ * @param {string} left - First network name
1198
+ * @param {string} right - Second network name
1199
+ * @returns {boolean} True if both belong to the same chain family
1200
+ */
1201
+ function areAddressNetworksCompatible(left, right) {
1202
+ return resolveAddressNetworkFamily(left) === resolveAddressNetworkFamily(right);
1203
+ }
1204
+
1205
+ /**
1206
+ * Detect network from address prefix.
1207
+ * `nq1...` / `tnq1...` are AuthScript witness-v1 destinations.
1208
+ *
1166
1209
  * @param {string} address - Neurai address
1167
1210
  * @returns {string} Network name ('xna', 'xna-test', 'xna-pq', or 'xna-pq-test')
1168
1211
  */
1169
1212
  function detectNetworkFromAddress(address) {
1170
- if (address.startsWith(NETWORKS.MAINNET_PQ.pqAddressPrefix)) {
1213
+ if (address.startsWith(NETWORKS.MAINNET_PQ.authScriptAddressPrefix)) {
1171
1214
  return 'xna-pq';
1172
- } else if (address.startsWith(NETWORKS.TESTNET_PQ.pqAddressPrefix)) {
1215
+ } else if (address.startsWith(NETWORKS.TESTNET_PQ.authScriptAddressPrefix)) {
1173
1216
  return 'xna-pq-test';
1174
1217
  } else if (address.startsWith('N')) {
1175
1218
  return 'xna';
@@ -1182,9 +1225,13 @@ var NeuraiAssetsBundle = (function (exports) {
1182
1225
 
1183
1226
  networks = {
1184
1227
  NETWORKS,
1228
+ MAINNET_NETWORKS,
1229
+ TESTNET_NETWORKS,
1185
1230
  ASSET_NAME_RULES,
1186
1231
  ASSET_LIMITS,
1187
1232
  getNetworkConfig,
1233
+ resolveAddressNetworkFamily,
1234
+ areAddressNetworksCompatible,
1188
1235
  detectNetworkFromAddress
1189
1236
  };
1190
1237
  return networks;
@@ -1212,9 +1259,13 @@ var NeuraiAssetsBundle = (function (exports) {
1212
1259
  } = requireBurnAddresses();
1213
1260
  const {
1214
1261
  NETWORKS,
1262
+ MAINNET_NETWORKS,
1263
+ TESTNET_NETWORKS,
1215
1264
  ASSET_NAME_RULES,
1216
1265
  ASSET_LIMITS,
1217
1266
  getNetworkConfig,
1267
+ resolveAddressNetworkFamily,
1268
+ areAddressNetworksCompatible,
1218
1269
  detectNetworkFromAddress
1219
1270
  } = requireNetworks();
1220
1271
 
@@ -1237,9 +1288,13 @@ var NeuraiAssetsBundle = (function (exports) {
1237
1288
 
1238
1289
  // Networks
1239
1290
  NETWORKS,
1291
+ MAINNET_NETWORKS,
1292
+ TESTNET_NETWORKS,
1240
1293
  ASSET_NAME_RULES,
1241
1294
  ASSET_LIMITS,
1242
1295
  getNetworkConfig,
1296
+ resolveAddressNetworkFamily,
1297
+ areAddressNetworksCompatible,
1243
1298
  detectNetworkFromAddress
1244
1299
  };
1245
1300
  return constants$1;
@@ -1856,7 +1911,11 @@ var NeuraiAssetsBundle = (function (exports) {
1856
1911
  function requireNetworkDetector () {
1857
1912
  if (hasRequiredNetworkDetector) return networkDetector;
1858
1913
  hasRequiredNetworkDetector = 1;
1859
- const { NETWORKS } = requireConstants();
1914
+ const {
1915
+ NETWORKS,
1916
+ areAddressNetworksCompatible,
1917
+ resolveAddressNetworkFamily
1918
+ } = requireConstants();
1860
1919
 
1861
1920
  class NetworkDetector {
1862
1921
  /**
@@ -1893,18 +1952,18 @@ var NeuraiAssetsBundle = (function (exports) {
1893
1952
  /**
1894
1953
  * Detect network from address
1895
1954
  * @param {string} address - Neurai address
1896
- * @returns {string} Network name ('xna', 'xna-test', 'xna-pq', or 'xna-pq-test')
1955
+ * @returns {string} Network label ('xna', 'xna-test', 'xna-pq', or 'xna-pq-test')
1897
1956
  */
1898
1957
  static detectFromAddress(address) {
1899
1958
  if (!address || typeof address !== 'string') {
1900
1959
  throw new Error('Address must be a non-empty string');
1901
1960
  }
1902
1961
 
1903
- if (address.startsWith(NETWORKS.MAINNET_PQ.pqAddressPrefix)) {
1962
+ if (address.startsWith(NETWORKS.MAINNET_PQ.authScriptAddressPrefix)) {
1904
1963
  return 'xna-pq';
1905
1964
  }
1906
1965
 
1907
- if (address.startsWith(NETWORKS.TESTNET_PQ.pqAddressPrefix)) {
1966
+ if (address.startsWith(NETWORKS.TESTNET_PQ.authScriptAddressPrefix)) {
1908
1967
  return 'xna-pq-test';
1909
1968
  }
1910
1969
 
@@ -1924,31 +1983,32 @@ var NeuraiAssetsBundle = (function (exports) {
1924
1983
  /**
1925
1984
  * Detect network from multiple addresses
1926
1985
  * @param {string[]} addresses - Array of addresses
1927
- * @returns {string} Network name ('xna', 'xna-test', 'xna-pq', or 'xna-pq-test')
1986
+ * @returns {string} Network label. Mixed legacy/AuthScript addresses on the same
1987
+ * chain are normalized to the chain family label (`xna` or `xna-test`).
1928
1988
  */
1929
1989
  static detectFromAddresses(addresses) {
1930
1990
  if (!Array.isArray(addresses) || addresses.length === 0) {
1931
1991
  throw new Error('Addresses must be a non-empty array');
1932
1992
  }
1933
1993
 
1934
- // Detect from first address
1935
- const network = this.detectFromAddress(addresses[0]);
1994
+ const firstNetwork = this.detectFromAddress(addresses[0]);
1995
+ const family = resolveAddressNetworkFamily(firstNetwork);
1936
1996
 
1937
1997
  // Verify all addresses are from the same network
1938
1998
  for (let i = 1; i < addresses.length; i++) {
1939
1999
  const otherNetwork = this.detectFromAddress(addresses[i]);
1940
- if (otherNetwork !== network) {
1941
- throw new Error(`Mixed network addresses detected: ${network} and ${otherNetwork}`);
2000
+ if (!areAddressNetworksCompatible(firstNetwork, otherNetwork)) {
2001
+ throw new Error(`Mixed network addresses detected: ${firstNetwork} and ${otherNetwork}`);
1942
2002
  }
1943
2003
  }
1944
2004
 
1945
- return network;
2005
+ return family === 'mainnet' ? 'xna' : 'xna-test';
1946
2006
  }
1947
2007
 
1948
2008
  /**
1949
2009
  * Validate that addresses match expected network
1950
2010
  * @param {string[]} addresses - Array of addresses
1951
- * @param {string} expectedNetwork - Expected network ('xna', 'xna-test', 'xna-pq', or 'xna-pq-test')
2011
+ * @param {string} expectedNetwork - Expected network label
1952
2012
  * @returns {boolean} True if all addresses match network
1953
2013
  */
1954
2014
  static validateAddressesNetwork(addresses, expectedNetwork) {
@@ -1958,7 +2018,7 @@ var NeuraiAssetsBundle = (function (exports) {
1958
2018
 
1959
2019
  for (const address of addresses) {
1960
2020
  const network = this.detectFromAddress(address);
1961
- if (network !== expectedNetwork) {
2021
+ if (!areAddressNetworksCompatible(network, expectedNetwork)) {
1962
2022
  throw new Error(
1963
2023
  `Address ${address} is from ${network} but expected ${expectedNetwork}`
1964
2024
  );
@@ -4376,23 +4436,27 @@ var NeuraiAssetsBundle = (function (exports) {
4376
4436
  }
4377
4437
 
4378
4438
  /**
4379
- * Convert amount to satoshis
4380
- * @param {number} amount - Amount in asset units
4381
- * @param {number} units - Decimal places
4382
- * @returns {number} Amount in satoshis
4439
+ * Convert asset amount to protocol raw units.
4440
+ * Asset raw quantities in transaction payloads are always encoded with
4441
+ * 8 decimal places, regardless of the asset's displayed `units`.
4442
+ *
4443
+ * @param {number} amount - User-facing asset amount
4444
+ * @param {number} units - Asset decimal places (kept for API compatibility)
4445
+ * @returns {number} Amount in protocol raw units
4383
4446
  */
4384
4447
  toSatoshis(amount, units) {
4385
- return Math.round(amount * Math.pow(10, units));
4448
+ return Math.round(amount * 100000000);
4386
4449
  }
4387
4450
 
4388
4451
  /**
4389
- * Convert satoshis to amount
4390
- * @param {number} satoshis - Amount in satoshis
4391
- * @param {number} units - Decimal places
4452
+ * Convert protocol raw units back to a user-facing asset amount.
4453
+ *
4454
+ * @param {number} satoshis - Amount in protocol raw units
4455
+ * @param {number} units - Asset decimal places (kept for API compatibility)
4392
4456
  * @returns {number} Amount in asset units
4393
4457
  */
4394
4458
  fromSatoshis(satoshis, units) {
4395
- return satoshis / Math.pow(10, units);
4459
+ return satoshis / 100000000;
4396
4460
  }
4397
4461
 
4398
4462
  /**
@@ -5975,11 +6039,13 @@ var NeuraiAssetsBundle = (function (exports) {
5975
6039
  // Last: Issue qualifier operation
5976
6040
  const issueQualifierOutput = OutputFormatter.formatIssueQualifierOutput({
5977
6041
  asset_name: assetName,
5978
- asset_quantity: quantity,
6042
+ asset_quantity: this.toSatoshis(quantity, 0),
5979
6043
  has_ipfs: hasIpfs,
5980
6044
  ipfs_hash: ipfsHash,
5981
6045
  root_change_address: isSub ? changeAddress : undefined,
5982
- change_quantity: isSub ? parentQualifierQuantity : undefined
6046
+ change_quantity: isSub && parentQualifierQuantity !== null
6047
+ ? this.toSatoshis(parentQualifierQuantity, 0)
6048
+ : undefined
5983
6049
  });
5984
6050
 
5985
6051
  outputs.push({ [toAddress]: issueQualifierOutput });
@@ -6741,12 +6807,12 @@ var NeuraiAssetsBundle = (function (exports) {
6741
6807
  ? OutputFormatter.formatUntagAddressesOutput({
6742
6808
  qualifier: qualifierName,
6743
6809
  addresses: targetAddresses,
6744
- change_quantity: qualifierQuantity
6810
+ change_quantity: this.toSatoshis(qualifierQuantity, 0)
6745
6811
  })
6746
6812
  : OutputFormatter.formatTagAddressesOutput({
6747
6813
  qualifier: qualifierName,
6748
6814
  addresses: targetAddresses,
6749
- change_quantity: qualifierQuantity
6815
+ change_quantity: this.toSatoshis(qualifierQuantity, 0)
6750
6816
  });
6751
6817
 
6752
6818
  outputs.push({ [changeAddress]: operationOutput });