@neuraiproject/neurai-assets 1.0.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.
Files changed (58) hide show
  1. package/README.md +522 -0
  2. package/examples/01-create-root-asset.js +71 -0
  3. package/examples/02-create-sub-asset.js +79 -0
  4. package/examples/03-create-nfts.js +140 -0
  5. package/examples/04-reissue-asset.js +164 -0
  6. package/examples/05-create-qualifier-and-tag.js +209 -0
  7. package/examples/06-create-restricted-asset.js +223 -0
  8. package/examples/07-freeze-and-unfreeze.js +292 -0
  9. package/examples/08-query-assets.js +332 -0
  10. package/examples/09-wallet-integration.js +320 -0
  11. package/examples/README.md +319 -0
  12. package/package.json +43 -0
  13. package/src/NeuraiAssets.js +468 -0
  14. package/src/builders/BaseAssetTransactionBuilder.js +303 -0
  15. package/src/builders/FreezeAddressBuilder.js +271 -0
  16. package/src/builders/IssueQualifierBuilder.js +251 -0
  17. package/src/builders/IssueRestrictedBuilder.js +187 -0
  18. package/src/builders/IssueRootBuilder.js +173 -0
  19. package/src/builders/IssueSubBuilder.js +237 -0
  20. package/src/builders/IssueUniqueBuilder.js +255 -0
  21. package/src/builders/ReissueBuilder.js +246 -0
  22. package/src/builders/ReissueRestrictedBuilder.js +264 -0
  23. package/src/builders/TagAddressBuilder.js +243 -0
  24. package/src/builders/index.js +38 -0
  25. package/src/constants/assetTypes.js +23 -0
  26. package/src/constants/burnAddresses.js +65 -0
  27. package/src/constants/fees.js +61 -0
  28. package/src/constants/index.js +44 -0
  29. package/src/constants/networks.js +112 -0
  30. package/src/errors/AssetErrors.js +135 -0
  31. package/src/errors/ValidationErrors.js +87 -0
  32. package/src/errors/index.js +56 -0
  33. package/src/index.js +68 -0
  34. package/src/managers/BurnManager.js +222 -0
  35. package/src/managers/OutputOrderer.js +289 -0
  36. package/src/managers/OwnerTokenManager.js +265 -0
  37. package/src/managers/UTXOSelector.js +309 -0
  38. package/src/managers/index.js +16 -0
  39. package/src/queries/AssetQueries.js +447 -0
  40. package/src/queries/index.js +10 -0
  41. package/src/utils/amountConverter.js +115 -0
  42. package/src/utils/assetNameParser.js +203 -0
  43. package/src/utils/index.js +16 -0
  44. package/src/utils/networkDetector.js +144 -0
  45. package/src/utils/outputFormatter.js +292 -0
  46. package/src/validators/amountValidator.js +149 -0
  47. package/src/validators/assetNameValidator.js +296 -0
  48. package/src/validators/index.js +16 -0
  49. package/src/validators/ipfsValidator.js +101 -0
  50. package/src/validators/verifierValidator.js +146 -0
  51. package/tests/README.md +126 -0
  52. package/tests/integration/assetLifecycle.test.js +244 -0
  53. package/tests/mocks/rpcMock.js +156 -0
  54. package/tests/unit/NeuraiAssets.test.js +217 -0
  55. package/tests/unit/utils/amountConverter.test.js +171 -0
  56. package/tests/unit/utils/assetNameParser.test.js +203 -0
  57. package/tests/unit/validators/amountValidator.test.js +143 -0
  58. package/tests/unit/validators/assetNameValidator.test.js +228 -0
@@ -0,0 +1,468 @@
1
+ /**
2
+ * NeuraiAssets - Main API Class
3
+ * Unified interface for all Neurai asset operations
4
+ *
5
+ * Usage:
6
+ * const assets = new NeuraiAssets(rpc, {
7
+ * network: 'xna',
8
+ * addresses: ['N...'],
9
+ * changeAddress: 'N...',
10
+ * toAddress: 'N...'
11
+ * });
12
+ *
13
+ * // Create asset
14
+ * const result = await assets.createRootAsset({
15
+ * assetName: 'MYTOKEN',
16
+ * quantity: 1000000,
17
+ * units: 2
18
+ * });
19
+ *
20
+ * // Query asset
21
+ * const assetData = await assets.getAssetData('MYTOKEN');
22
+ */
23
+
24
+ const { AssetQueries } = require('./queries');
25
+ const {
26
+ IssueRootBuilder,
27
+ IssueSubBuilder,
28
+ IssueUniqueBuilder,
29
+ IssueQualifierBuilder,
30
+ IssueRestrictedBuilder,
31
+ ReissueBuilder,
32
+ ReissueRestrictedBuilder,
33
+ TagAddressBuilder,
34
+ FreezeAddressBuilder
35
+ } = require('./builders');
36
+
37
+ class NeuraiAssets {
38
+ /**
39
+ * @param {Function} rpc - RPC function to call Neurai node
40
+ * @param {object} config - Configuration options
41
+ * @param {string} config.network - Network identifier ('xna' or 'xna-test')
42
+ * @param {Array<string>} config.addresses - Wallet addresses
43
+ * @param {string} config.changeAddress - Default change address
44
+ * @param {string} config.toAddress - Default receiving address
45
+ */
46
+ constructor(rpc, config = {}) {
47
+ if (!rpc || typeof rpc !== 'function') {
48
+ throw new Error('RPC function is required');
49
+ }
50
+
51
+ this.rpc = rpc;
52
+ this.config = {
53
+ network: config.network || 'xna',
54
+ addresses: config.addresses || [],
55
+ changeAddress: config.changeAddress || null,
56
+ toAddress: config.toAddress || null
57
+ };
58
+
59
+ // Initialize query interface
60
+ this.queries = new AssetQueries(rpc);
61
+ }
62
+
63
+ /**
64
+ * Update configuration
65
+ * @param {object} config - New configuration
66
+ */
67
+ updateConfig(config) {
68
+ Object.assign(this.config, config);
69
+ }
70
+
71
+ /**
72
+ * Build transaction parameters object
73
+ * @param {object} params - Operation-specific parameters
74
+ * @returns {object} Complete parameters with config
75
+ */
76
+ _buildParams(params) {
77
+ return {
78
+ ...params,
79
+ network: this.config.network,
80
+ addresses: this.config.addresses,
81
+ changeAddress: params.changeAddress || this.config.changeAddress,
82
+ toAddress: params.toAddress || this.config.toAddress
83
+ };
84
+ }
85
+
86
+ // ========================================
87
+ // ROOT ASSET OPERATIONS
88
+ // ========================================
89
+
90
+ /**
91
+ * Create a ROOT asset
92
+ * @param {object} params - Asset creation parameters
93
+ * @param {string} params.assetName - Asset name (3-30 chars, A-Z 0-9 _ .)
94
+ * @param {number} params.quantity - Total supply
95
+ * @param {number} [params.units=0] - Decimal places (0-8)
96
+ * @param {boolean} [params.reissuable=true] - Can mint more later
97
+ * @param {boolean} [params.hasIpfs=false] - Has IPFS metadata
98
+ * @param {string} [params.ipfsHash] - IPFS hash (if hasIpfs=true)
99
+ * @returns {Promise<object>} Transaction data
100
+ */
101
+ async createRootAsset(params) {
102
+ const builder = new IssueRootBuilder(this.rpc, this._buildParams(params));
103
+ return await builder.build();
104
+ }
105
+
106
+ /**
107
+ * Create a SUB asset
108
+ * @param {object} params - Sub-asset creation parameters
109
+ * @param {string} params.assetName - Asset name (ROOT/SUB format)
110
+ * @param {number} params.quantity - Total supply
111
+ * @param {number} [params.units=0] - Decimal places (0-8)
112
+ * @param {boolean} [params.reissuable=true] - Can mint more later
113
+ * @param {boolean} [params.hasIpfs=false] - Has IPFS metadata
114
+ * @param {string} [params.ipfsHash] - IPFS hash (if hasIpfs=true)
115
+ * @returns {Promise<object>} Transaction data
116
+ */
117
+ async createSubAsset(params) {
118
+ const builder = new IssueSubBuilder(this.rpc, this._buildParams(params));
119
+ return await builder.build();
120
+ }
121
+
122
+ /**
123
+ * Reissue (mint more) of a ROOT or SUB asset
124
+ * @param {object} params - Reissue parameters
125
+ * @param {string} params.assetName - Asset name
126
+ * @param {number} params.quantity - Amount to mint
127
+ * @param {boolean} [params.reissuable] - Lock supply if false
128
+ * @param {string} [params.newIpfs] - Update IPFS hash
129
+ * @returns {Promise<object>} Transaction data
130
+ */
131
+ async reissueAsset(params) {
132
+ const builder = new ReissueBuilder(this.rpc, this._buildParams(params));
133
+ return await builder.build();
134
+ }
135
+
136
+ // ========================================
137
+ // UNIQUE ASSET (NFT) OPERATIONS
138
+ // ========================================
139
+
140
+ /**
141
+ * Create UNIQUE assets (NFTs)
142
+ * @param {object} params - NFT creation parameters
143
+ * @param {string} params.rootAssetName - Root asset name
144
+ * @param {Array<object>} params.assetTags - NFT tags and metadata
145
+ * @param {string} params.assetTags[].tag - Unique identifier for this NFT
146
+ * @param {boolean} [params.assetTags[].hasIpfs=false] - Has IPFS metadata
147
+ * @param {string} [params.assetTags[].ipfsHash] - IPFS hash for this NFT
148
+ * @returns {Promise<object>} Transaction data
149
+ */
150
+ async createUniqueAssets(params) {
151
+ const builder = new IssueUniqueBuilder(this.rpc, this._buildParams(params));
152
+ return await builder.build();
153
+ }
154
+
155
+ // ========================================
156
+ // QUALIFIER OPERATIONS (KYC Tags)
157
+ // ========================================
158
+
159
+ /**
160
+ * Create a QUALIFIER (root or sub)
161
+ * @param {object} params - Qualifier creation parameters
162
+ * @param {string} params.qualifierName - Qualifier name (#NAME or #ROOT/#SUB)
163
+ * @param {number} [params.quantity=1] - Quantity (1-10)
164
+ * @param {boolean} [params.hasIpfs=false] - Has IPFS metadata
165
+ * @param {string} [params.ipfsHash] - IPFS hash
166
+ * @param {string} [params.changeAddress] - Override change address
167
+ * @returns {Promise<object>} Transaction data
168
+ */
169
+ async createQualifier(params) {
170
+ const builder = new IssueQualifierBuilder(this.rpc, this._buildParams(params));
171
+ return await builder.build();
172
+ }
173
+
174
+ /**
175
+ * Assign qualifier tag(s) to address(es)
176
+ * @param {object} params - Tag assignment parameters
177
+ * @param {string} params.qualifierName - Qualifier name (#NAME)
178
+ * @param {Array<string>} params.addresses - Addresses to tag
179
+ * @param {string} [params.assetData=''] - Optional data
180
+ * @returns {Promise<object>} Transaction data
181
+ */
182
+ async tagAddresses(params) {
183
+ const builder = new TagAddressBuilder(this.rpc, this._buildParams(params));
184
+ return await builder.build();
185
+ }
186
+
187
+ /**
188
+ * Remove qualifier tag(s) from address(es)
189
+ * @param {object} params - Tag removal parameters
190
+ * @param {string} params.qualifierName - Qualifier name (#NAME)
191
+ * @param {Array<string>} params.addresses - Addresses to untag
192
+ * @returns {Promise<object>} Transaction data
193
+ */
194
+ async untagAddresses(params) {
195
+ const builder = new TagAddressBuilder(this.rpc, this._buildParams(params));
196
+ return await builder.buildUntag();
197
+ }
198
+
199
+ // ========================================
200
+ // RESTRICTED ASSET OPERATIONS (Security Tokens)
201
+ // ========================================
202
+
203
+ /**
204
+ * Create a RESTRICTED asset (security token)
205
+ * @param {object} params - Restricted asset creation parameters
206
+ * @param {string} params.assetName - Asset name ($NAME format)
207
+ * @param {number} params.quantity - Total supply
208
+ * @param {string} params.verifierString - Boolean logic for compliance (e.g., "#KYC & #ACCREDITED")
209
+ * @param {number} [params.units=0] - Decimal places (0-8)
210
+ * @param {boolean} [params.reissuable=true] - Can mint more later
211
+ * @param {boolean} [params.hasIpfs=false] - Has IPFS metadata
212
+ * @param {string} [params.ipfsHash] - IPFS hash
213
+ * @returns {Promise<object>} Transaction data
214
+ */
215
+ async createRestrictedAsset(params) {
216
+ const builder = new IssueRestrictedBuilder(this.rpc, this._buildParams(params));
217
+ return await builder.build();
218
+ }
219
+
220
+ /**
221
+ * Reissue (mint more) of a RESTRICTED asset
222
+ * @param {object} params - Reissue parameters
223
+ * @param {string} params.assetName - Restricted asset name ($NAME)
224
+ * @param {number} params.quantity - Amount to mint
225
+ * @param {boolean} [params.changeVerifier=false] - Update verifier string
226
+ * @param {string} [params.newVerifier] - New verifier string (if changeVerifier=true)
227
+ * @param {boolean} [params.reissuable] - Lock supply if false
228
+ * @param {string} [params.newIpfs] - Update IPFS hash
229
+ * @returns {Promise<object>} Transaction data
230
+ */
231
+ async reissueRestrictedAsset(params) {
232
+ const builder = new ReissueRestrictedBuilder(this.rpc, this._buildParams(params));
233
+ return await builder.build();
234
+ }
235
+
236
+ /**
237
+ * Freeze specific addresses for a restricted asset
238
+ * @param {object} params - Freeze parameters
239
+ * @param {string} params.assetName - Restricted asset name ($NAME)
240
+ * @param {Array<string>} params.addresses - Addresses to freeze
241
+ * @returns {Promise<object>} Transaction data
242
+ */
243
+ async freezeAddresses(params) {
244
+ const builder = new FreezeAddressBuilder(this.rpc, this._buildParams(params));
245
+ return await builder.build();
246
+ }
247
+
248
+ /**
249
+ * Unfreeze specific addresses for a restricted asset
250
+ * @param {object} params - Unfreeze parameters
251
+ * @param {string} params.assetName - Restricted asset name ($NAME)
252
+ * @param {Array<string>} params.addresses - Addresses to unfreeze
253
+ * @returns {Promise<object>} Transaction data
254
+ */
255
+ async unfreezeAddresses(params) {
256
+ const builder = new FreezeAddressBuilder(this.rpc, this._buildParams(params));
257
+ return await builder.buildUnfreeze();
258
+ }
259
+
260
+ /**
261
+ * Freeze entire restricted asset globally
262
+ * @param {object} params - Global freeze parameters
263
+ * @param {string} params.assetName - Restricted asset name ($NAME)
264
+ * @returns {Promise<object>} Transaction data
265
+ */
266
+ async freezeAssetGlobally(params) {
267
+ const builder = new FreezeAddressBuilder(this.rpc, this._buildParams(params));
268
+ return await builder.buildGlobalFreeze();
269
+ }
270
+
271
+ /**
272
+ * Unfreeze entire restricted asset globally
273
+ * @param {object} params - Global unfreeze parameters
274
+ * @param {string} params.assetName - Restricted asset name ($NAME)
275
+ * @returns {Promise<object>} Transaction data
276
+ */
277
+ async unfreezeAssetGlobally(params) {
278
+ const builder = new FreezeAddressBuilder(this.rpc, this._buildParams(params));
279
+ return await builder.buildGlobalUnfreeze();
280
+ }
281
+
282
+ // ========================================
283
+ // QUERY OPERATIONS
284
+ // Delegates to AssetQueries instance
285
+ // ========================================
286
+ // Note: Asset transfers are handled by neurai-jswallet
287
+
288
+ /**
289
+ * Get asset metadata
290
+ * @param {string} assetName - Asset name
291
+ * @returns {Promise<object>} Asset data
292
+ */
293
+ async getAssetData(assetName) {
294
+ return await this.queries.getAssetData(assetName);
295
+ }
296
+
297
+ /**
298
+ * List all assets on blockchain
299
+ * @param {string} [filter='*'] - Filter pattern (e.g., 'MY*')
300
+ * @param {boolean} [verbose=false] - Include detailed information
301
+ * @param {number} [count=100] - Maximum number to return
302
+ * @param {number} [start=0] - Starting index for pagination
303
+ * @returns {Promise<Array|object>} Array of asset names or detailed objects
304
+ */
305
+ async listAssets(filter = '*', verbose = false, count = 100, start = 0) {
306
+ return await this.queries.listAssets(filter, verbose, count, start);
307
+ }
308
+
309
+ /**
310
+ * List assets owned by wallet
311
+ * @param {string} [assetName='*'] - Filter by asset name
312
+ * @param {boolean} [verbose=false] - Include detailed information
313
+ * @param {number} [count=100] - Maximum number to return
314
+ * @param {number} [start=0] - Starting index
315
+ * @param {number} [confs=1] - Minimum confirmations
316
+ * @returns {Promise<object>} Object with asset names as keys and amounts as values
317
+ */
318
+ async listMyAssets(assetName = '*', verbose = false, count = 100, start = 0, confs = 1) {
319
+ return await this.queries.listMyAssets(assetName, verbose, count, start, confs);
320
+ }
321
+
322
+ /**
323
+ * List all addresses holding a specific asset
324
+ * @param {string} assetName - Asset name
325
+ * @param {boolean} [onlyCount=false] - Return only count instead of full list
326
+ * @param {number} [count=100] - Maximum number to return
327
+ * @param {number} [start=0] - Starting index
328
+ * @returns {Promise<Array|number>} Array of {address, amount} or count
329
+ */
330
+ async listAddressesByAsset(assetName, onlyCount = false, count = 100, start = 0) {
331
+ return await this.queries.listAddressesByAsset(assetName, onlyCount, count, start);
332
+ }
333
+
334
+ /**
335
+ * List asset balances for a specific address
336
+ * @param {string} address - Address to query
337
+ * @param {boolean} [onlyTotal=false] - Return only count instead of full list
338
+ * @param {number} [count=100] - Maximum number to return
339
+ * @param {number} [start=0] - Starting index
340
+ * @returns {Promise<Array|number>} Array of {asset, amount} or count
341
+ */
342
+ async listAssetBalancesByAddress(address, onlyTotal = false, count = 100, start = 0) {
343
+ return await this.queries.listAssetBalancesByAddress(address, onlyTotal, count, start);
344
+ }
345
+
346
+ /**
347
+ * Check if an address has a specific qualifier tag
348
+ * @param {string} address - Address to check
349
+ * @param {string} qualifierName - Qualifier name (e.g., '#KYC_VERIFIED')
350
+ * @returns {Promise<boolean>} True if address has the tag
351
+ */
352
+ async checkAddressTag(address, qualifierName) {
353
+ return await this.queries.checkAddressTag(address, qualifierName);
354
+ }
355
+
356
+ /**
357
+ * List all qualifiers assigned to an address
358
+ * @param {string} address - Address to query
359
+ * @returns {Promise<Array>} Array of qualifier names
360
+ */
361
+ async listTagsForAddress(address) {
362
+ return await this.queries.listTagsForAddress(address);
363
+ }
364
+
365
+ /**
366
+ * List all addresses with a specific qualifier tag
367
+ * @param {string} qualifierName - Qualifier name
368
+ * @returns {Promise<Array>} Array of addresses
369
+ */
370
+ async listAddressesForTag(qualifierName) {
371
+ return await this.queries.listAddressesForTag(qualifierName);
372
+ }
373
+
374
+ /**
375
+ * Check if an address can hold a restricted asset
376
+ * @param {string} address - Address to check
377
+ * @param {string} restrictedAssetName - Restricted asset name (e.g., '$SECURITY')
378
+ * @returns {Promise<boolean>} True if address meets verifier requirements
379
+ */
380
+ async checkAddressRestriction(address, restrictedAssetName) {
381
+ return await this.queries.checkAddressRestriction(address, restrictedAssetName);
382
+ }
383
+
384
+ /**
385
+ * Check if an address is frozen for a restricted asset
386
+ * @param {string} address - Address to check
387
+ * @param {string} restrictedAssetName - Restricted asset name
388
+ * @returns {Promise<boolean>} True if address is frozen
389
+ */
390
+ async isAddressFrozen(address, restrictedAssetName) {
391
+ return await this.queries.isAddressFrozen(address, restrictedAssetName);
392
+ }
393
+
394
+ /**
395
+ * Check if an asset is globally frozen
396
+ * @param {string} restrictedAssetName - Restricted asset name
397
+ * @returns {Promise<boolean>} True if asset is globally frozen
398
+ */
399
+ async checkGlobalRestriction(restrictedAssetName) {
400
+ return await this.queries.checkGlobalRestriction(restrictedAssetName);
401
+ }
402
+
403
+ /**
404
+ * Get verifier string for a restricted asset
405
+ * @param {string} restrictedAssetName - Restricted asset name
406
+ * @returns {Promise<string>} Verifier string
407
+ */
408
+ async getVerifierString(restrictedAssetName) {
409
+ return await this.queries.getVerifierString(restrictedAssetName);
410
+ }
411
+
412
+ /**
413
+ * Validate verifier string syntax
414
+ * @param {string} verifierString - Verifier string to validate
415
+ * @returns {Promise<boolean>} True if valid
416
+ */
417
+ async isValidVerifierString(verifierString) {
418
+ return await this.queries.isValidVerifierString(verifierString);
419
+ }
420
+
421
+ /**
422
+ * Get snapshot of asset ownership at a specific block
423
+ * @param {string} assetName - Asset name
424
+ * @param {number} blockHeight - Block height for snapshot
425
+ * @returns {Promise<object>} Snapshot request result
426
+ */
427
+ async getSnapshotRequest(assetName, blockHeight) {
428
+ return await this.queries.getSnapshotRequest(assetName, blockHeight);
429
+ }
430
+
431
+ /**
432
+ * Cancel a snapshot request
433
+ * @param {string} assetName - Asset name
434
+ * @param {number} blockHeight - Block height of snapshot to cancel
435
+ * @returns {Promise<boolean>} True if cancelled successfully
436
+ */
437
+ async cancelSnapshotRequest(assetName, blockHeight) {
438
+ return await this.queries.cancelSnapshotRequest(assetName, blockHeight);
439
+ }
440
+
441
+ /**
442
+ * Check if asset exists
443
+ * @param {string} assetName - Asset name
444
+ * @returns {Promise<boolean>} True if asset exists
445
+ */
446
+ async assetExists(assetName) {
447
+ return await this.queries.assetExists(assetName);
448
+ }
449
+
450
+ /**
451
+ * Get asset type from name
452
+ * @param {string} assetName - Asset name
453
+ * @returns {string} Asset type ('ROOT', 'SUB', 'UNIQUE', 'QUALIFIER', 'RESTRICTED', 'OWNER')
454
+ */
455
+ getAssetType(assetName) {
456
+ return this.queries.getAssetType(assetName);
457
+ }
458
+
459
+ /**
460
+ * Get total count of assets on blockchain
461
+ * @returns {Promise<number>} Total asset count
462
+ */
463
+ async getAssetCount() {
464
+ return await this.queries.getAssetCount();
465
+ }
466
+ }
467
+
468
+ module.exports = NeuraiAssets;