@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
package/README.md ADDED
@@ -0,0 +1,522 @@
1
+ # @neuraiproject/neurai-assets
2
+
3
+ Complete asset management library for Neurai blockchain. Supports creation, reissuance, and queries for all asset types in a non-custodial way.
4
+
5
+ ## Features
6
+
7
+ - ✅ **Non-custodial**: Library builds unsigned transactions, your wallet signs them
8
+ - ✅ **All asset types**: ROOT, SUB, UNIQUE (NFTs), QUALIFIER, RESTRICTED
9
+ - ✅ **Complete operations**: Creation, reissuance, tagging, freezing
10
+ - ✅ **RPC queries**: Complete wrapper for all asset query methods
11
+ - ✅ **Client-side validation**: Prevents errors before creating transactions
12
+ - ✅ **Owner token protection**: Validation to prevent permanent loss
13
+
14
+ ## Supported Asset Types
15
+
16
+ | Type | Format | Cost | Description |
17
+ |------|---------|-------|-------------|
18
+ | **ROOT** | `MYTOKEN` | 1000 XNA | Standard token |
19
+ | **SUB** | `PARENT/SUB` | 200 XNA | Sub-token of a ROOT |
20
+ | **UNIQUE** | `ROOT#TAG` | 10 XNA | Unique NFT |
21
+ | **QUALIFIER** | `#KYC` | 2000 XNA | Compliance tag |
22
+ | **SUB_QUALIFIER** | `#PARENT/#SUB` | 200 XNA | Sub-qualifier |
23
+ | **RESTRICTED** | `$SECURITY` | 3000 XNA | Security token with compliance |
24
+
25
+ ## Installation
26
+
27
+ ```bash
28
+ npm install @neuraiproject/neurai-assets
29
+ ```
30
+
31
+ ## Basic Usage
32
+
33
+ ```javascript
34
+ const NeuraiAssets = require('@neuraiproject/neurai-assets');
35
+
36
+ // Initialize with RPC function
37
+ const assets = new NeuraiAssets(rpc, {
38
+ network: 'xna',
39
+ addresses: walletAddresses,
40
+ changeAddress: myChangeAddress,
41
+ toAddress: myReceivingAddress
42
+ });
43
+
44
+ // Create a ROOT asset
45
+ const result = await assets.createRootAsset({
46
+ assetName: 'MYTOKEN',
47
+ quantity: 1000000,
48
+ units: 2,
49
+ reissuable: true
50
+ });
51
+
52
+ // Sign and broadcast
53
+ const signedTx = await wallet.signTransaction(result.rawTx);
54
+ const txid = await wallet.broadcastTransaction(signedTx);
55
+ ```
56
+
57
+ ## Operation Examples
58
+
59
+ ### Create ROOT Asset
60
+
61
+ ```javascript
62
+ const result = await assets.createRootAsset({
63
+ assetName: 'MYTOKEN',
64
+ quantity: 1000000, // Total supply
65
+ units: 2, // Decimals (0-8)
66
+ reissuable: true, // Allow reissuance
67
+ hasIpfs: true,
68
+ ipfsHash: 'QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG'
69
+ });
70
+ ```
71
+
72
+ ### Create SUB Asset
73
+
74
+ ```javascript
75
+ // Requires the parent asset's owner token (MYTOKEN!)
76
+ const result = await assets.createSubAsset({
77
+ assetName: 'MYTOKEN/SUB',
78
+ quantity: 100000,
79
+ units: 0,
80
+ reissuable: true
81
+ });
82
+ ```
83
+
84
+ ### Reissue (Mint more supply)
85
+
86
+ ```javascript
87
+ // Requires the asset's owner token (MYTOKEN!)
88
+ const result = await assets.reissueAsset({
89
+ assetName: 'MYTOKEN',
90
+ quantity: 500000, // Additional amount to mint
91
+ reissuable: true, // false = lock supply permanently
92
+ newIpfs: 'Qm...' // Update IPFS (optional)
93
+ });
94
+ ```
95
+
96
+ ### Create UNIQUE Assets (NFTs)
97
+
98
+ ```javascript
99
+ const result = await assets.createUniqueAssets({
100
+ rootAssetName: 'MYTOKEN',
101
+ assetTags: [
102
+ {
103
+ tag: 'NFT001',
104
+ hasIpfs: true,
105
+ ipfsHash: 'QmNFT1...'
106
+ },
107
+ {
108
+ tag: 'NFT002',
109
+ hasIpfs: true,
110
+ ipfsHash: 'QmNFT2...'
111
+ }
112
+ ]
113
+ });
114
+ ```
115
+
116
+ ### Create QUALIFIER (KYC Tags)
117
+
118
+ ```javascript
119
+ const result = await assets.createQualifier({
120
+ qualifierName: '#KYC_VERIFIED',
121
+ quantity: 1,
122
+ hasIpfs: true,
123
+ ipfsHash: 'Qm...'
124
+ });
125
+ ```
126
+
127
+ ### Tag Addresses
128
+
129
+ ```javascript
130
+ // Requires the qualifier's owner token (#KYC_VERIFIED!)
131
+ const result = await assets.tagAddresses({
132
+ qualifierName: '#KYC_VERIFIED',
133
+ addresses: [
134
+ 'NAddress1...',
135
+ 'NAddress2...'
136
+ ],
137
+ assetData: 'KYC expires 2025-12-31'
138
+ });
139
+ ```
140
+
141
+ ### Untag Addresses
142
+
143
+ ```javascript
144
+ const result = await assets.untagAddresses({
145
+ qualifierName: '#KYC_VERIFIED',
146
+ addresses: ['NAddress1...']
147
+ });
148
+ ```
149
+
150
+ ### Create RESTRICTED Asset (Security Token)
151
+
152
+ ```javascript
153
+ const result = await assets.createRestrictedAsset({
154
+ assetName: '$SECURITY',
155
+ quantity: 1000000,
156
+ units: 2,
157
+ verifierString: '#KYC_VERIFIED & #ACCREDITED', // Boolean logic
158
+ reissuable: true,
159
+ hasIpfs: true,
160
+ ipfsHash: 'Qm...'
161
+ });
162
+ ```
163
+
164
+ ### Reissue RESTRICTED Asset
165
+
166
+ ```javascript
167
+ const result = await assets.reissueRestrictedAsset({
168
+ assetName: '$SECURITY',
169
+ quantity: 500000,
170
+ changeVerifier: true,
171
+ newVerifier: '(#KYC_VERIFIED & #ACCREDITED) | #INSTITUTIONAL',
172
+ reissuable: false // Lock supply
173
+ });
174
+ ```
175
+
176
+ ### Freeze Addresses
177
+
178
+ ```javascript
179
+ // Requires the restricted asset's owner token ($SECURITY!)
180
+ const result = await assets.freezeAddresses({
181
+ assetName: '$SECURITY',
182
+ addresses: ['NAddress1...', 'NAddress2...']
183
+ });
184
+ ```
185
+
186
+ ### Unfreeze Addresses
187
+
188
+ ```javascript
189
+ const result = await assets.unfreezeAddresses({
190
+ assetName: '$SECURITY',
191
+ addresses: ['NAddress1...']
192
+ });
193
+ ```
194
+
195
+ ### Freeze Asset Globally
196
+
197
+ ```javascript
198
+ const result = await assets.freezeAssetGlobally({
199
+ assetName: '$SECURITY'
200
+ });
201
+ ```
202
+
203
+ ### Unfreeze Asset Globally
204
+
205
+ ```javascript
206
+ const result = await assets.unfreezeAssetGlobally({
207
+ assetName: '$SECURITY'
208
+ });
209
+ ```
210
+
211
+ ## Queries
212
+
213
+ ### Get Asset Metadata
214
+
215
+ ```javascript
216
+ const assetData = await assets.getAssetData('MYTOKEN');
217
+ console.log(assetData);
218
+ // {
219
+ // name: 'MYTOKEN',
220
+ // amount: 1000000,
221
+ // units: 2,
222
+ // reissuable: true,
223
+ // has_ipfs: true,
224
+ // ipfs_hash: 'Qm...'
225
+ // }
226
+ ```
227
+
228
+ ### List All Assets
229
+
230
+ ```javascript
231
+ const allAssets = await assets.listAssets('*', false, 100, 0);
232
+ // Returns array of asset names
233
+
234
+ // With details
235
+ const detailed = await assets.listAssets('MY*', true, 100, 0);
236
+ // Returns object with complete metadata
237
+ ```
238
+
239
+ ### List My Assets
240
+
241
+ ```javascript
242
+ const myAssets = await assets.listMyAssets();
243
+ console.log(myAssets);
244
+ // {
245
+ // 'MYTOKEN': 1000.00,
246
+ // 'ANOTHER': 500.50
247
+ // }
248
+ ```
249
+
250
+ ### View Asset Holders
251
+
252
+ ```javascript
253
+ const holders = await assets.listAddressesByAsset('MYTOKEN');
254
+ console.log(holders);
255
+ // [
256
+ // { address: 'NAddress1...', amount: 500.00 },
257
+ // { address: 'NAddress2...', amount: 300.00 }
258
+ // ]
259
+
260
+ // Count only
261
+ const count = await assets.listAddressesByAsset('MYTOKEN', true);
262
+ console.log(count); // 2
263
+ ```
264
+
265
+ ### View Address Assets
266
+
267
+ ```javascript
268
+ const balances = await assets.listAssetBalancesByAddress('NAddress1...');
269
+ console.log(balances);
270
+ // [
271
+ // { asset: 'MYTOKEN', amount: 500.00 },
272
+ // { asset: 'ANOTHER', amount: 100.00 }
273
+ // ]
274
+ ```
275
+
276
+ ### Check Address Tags
277
+
278
+ ```javascript
279
+ // Check if address has a specific tag
280
+ const hasTag = await assets.checkAddressTag('NAddress1...', '#KYC_VERIFIED');
281
+ console.log(hasTag); // true/false
282
+
283
+ // List all tags for an address
284
+ const tags = await assets.listTagsForAddress('NAddress1...');
285
+ console.log(tags); // ['#KYC_VERIFIED', '#ACCREDITED']
286
+ ```
287
+
288
+ ### Check Restrictions
289
+
290
+ ```javascript
291
+ // Check if address can receive restricted asset
292
+ const canReceive = await assets.checkAddressRestriction('NAddress1...', '$SECURITY');
293
+ console.log(canReceive); // true/false
294
+
295
+ // Check if address is frozen
296
+ const isFrozen = await assets.isAddressFrozen('NAddress1...', '$SECURITY');
297
+ console.log(isFrozen); // true/false
298
+
299
+ // Check if asset is globally frozen
300
+ const isGloballyFrozen = await assets.checkGlobalRestriction('$SECURITY');
301
+ console.log(isGloballyFrozen); // true/false
302
+ ```
303
+
304
+ ### View Verifier String
305
+
306
+ ```javascript
307
+ const verifier = await assets.getVerifierString('$SECURITY');
308
+ console.log(verifier); // '#KYC_VERIFIED & #ACCREDITED'
309
+
310
+ // Validate verifier syntax
311
+ const isValid = await assets.isValidVerifierString('(#KYC | #AML) & #ACCREDITED');
312
+ console.log(isValid); // true/false
313
+ ```
314
+
315
+ ### Check if Asset Exists
316
+
317
+ ```javascript
318
+ const exists = await assets.assetExists('MYTOKEN');
319
+ console.log(exists); // true/false
320
+ ```
321
+
322
+ ### Detect Asset Type
323
+
324
+ ```javascript
325
+ const type = assets.getAssetType('MYTOKEN'); // 'ROOT'
326
+ const type2 = assets.getAssetType('PARENT/SUB'); // 'SUB'
327
+ const type3 = assets.getAssetType('TOKEN#NFT'); // 'UNIQUE'
328
+ const type4 = assets.getAssetType('#KYC'); // 'QUALIFIER'
329
+ const type5 = assets.getAssetType('$SECURITY'); // 'RESTRICTED'
330
+ const type6 = assets.getAssetType('MYTOKEN!'); // 'OWNER'
331
+ ```
332
+
333
+ ## Transaction Result Structure
334
+
335
+ All creation/reissuance operations return an object with this structure:
336
+
337
+ ```javascript
338
+ {
339
+ rawTx: 'hex string', // Unsigned transaction (to sign with wallet)
340
+ inputs: [...], // Transaction inputs
341
+ outputs: {...}, // Ordered outputs
342
+ fee: 0.001, // Fee in XNA
343
+ burn: 1000, // Burned amount in XNA
344
+ metadata: { // Operation-specific metadata
345
+ assetName: 'MYTOKEN',
346
+ ownerTokenName: 'MYTOKEN!',
347
+ operationType: 'ISSUE_ROOT'
348
+ }
349
+ }
350
+ ```
351
+
352
+ ## Owner Tokens - IMPORTANT
353
+
354
+ When you create an asset, an **owner token** is automatically generated (e.g., `MYTOKEN!`).
355
+
356
+ ⚠️ **CRITICAL**: The owner token is required to:
357
+ - Reissue (mint more supply)
358
+ - Create SUB assets
359
+ - Manage tags (if qualifier)
360
+ - Freeze/unfreeze (if restricted)
361
+
362
+ ⚠️ **If you lose the owner token, you lose these capabilities PERMANENTLY**
363
+
364
+ The library automatically validates that the owner token is returned in each operation to prevent accidental loss.
365
+
366
+ ## Operation Costs
367
+
368
+ | Operation | Cost (XNA burned) |
369
+ |-----------|---------------------|
370
+ | Create ROOT asset | 1000 |
371
+ | Create SUB asset | 200 |
372
+ | Create UNIQUE asset | 10 (per NFT) |
373
+ | Create QUALIFIER (root) | 2000 |
374
+ | Create QUALIFIER (sub) | 200 |
375
+ | Create RESTRICTED asset | 3000 |
376
+ | Reissue ROOT/SUB | 200 |
377
+ | Reissue RESTRICTED | 200 |
378
+ | Tag/Untag address | 0.1 (per address) |
379
+ | Freeze/Unfreeze address | 0 (network fee only) |
380
+ | Freeze/Unfreeze global | 0 (network fee only) |
381
+
382
+ **Note**: In addition to the burned cost, all operations pay a network fee (calculated automatically).
383
+
384
+ ## Validations
385
+
386
+ The library validates client-side:
387
+
388
+ ✅ Asset names (format, length, allowed characters)
389
+ ✅ Amounts (not exceeding max supply of 21 billion)
390
+ ✅ Decimals (0-8)
391
+ ✅ IPFS hashes (valid format)
392
+ ✅ Verifier strings (boolean logic syntax)
393
+ ✅ Sufficient funds (XNA and assets)
394
+ ✅ Required owner tokens
395
+ ✅ Owner tokens returned (prevents loss)
396
+ ✅ Address prefixes by network
397
+
398
+ ## Network Configuration
399
+
400
+ ```javascript
401
+ // Mainnet
402
+ const assets = new NeuraiAssets(rpc, {
403
+ network: 'xna',
404
+ addresses: [...],
405
+ changeAddress: 'N...',
406
+ toAddress: 'N...'
407
+ });
408
+
409
+ // Testnet
410
+ const assets = new NeuraiAssets(rpc, {
411
+ network: 'xna-test',
412
+ addresses: [...],
413
+ changeAddress: 'm...' // or 'n...'
414
+ toAddress: 'm...'
415
+ });
416
+ ```
417
+
418
+ ## Update Configuration
419
+
420
+ ```javascript
421
+ assets.updateConfig({
422
+ addresses: newAddresses,
423
+ changeAddress: newChangeAddress
424
+ });
425
+ ```
426
+
427
+ ## Advanced API
428
+
429
+ For advanced usage, you can use builders directly:
430
+
431
+ ```javascript
432
+ const { builders } = require('@neuraiproject/neurai-assets');
433
+
434
+ const builder = new builders.IssueRootBuilder(rpc, {
435
+ assetName: 'MYTOKEN',
436
+ quantity: 1000000,
437
+ units: 2,
438
+ network: 'xna',
439
+ addresses: [...],
440
+ changeAddress: '...',
441
+ toAddress: '...'
442
+ });
443
+
444
+ const result = await builder.build();
445
+ ```
446
+
447
+ ## Error Handling
448
+
449
+ The library throws specific errors:
450
+
451
+ ```javascript
452
+ const { errors } = require('@neuraiproject/neurai-assets');
453
+
454
+ try {
455
+ await assets.createRootAsset({...});
456
+ } catch (error) {
457
+ if (error instanceof errors.AssetExistsError) {
458
+ console.error('Asset already exists');
459
+ } else if (error instanceof errors.InsufficientFundsError) {
460
+ console.error('Insufficient funds');
461
+ } else if (error instanceof errors.OwnerTokenNotFoundError) {
462
+ console.error('You do not have the required owner token');
463
+ } else if (error instanceof errors.OwnerTokenNotReturnedError) {
464
+ console.error('CRITICAL: Owner token not returned');
465
+ }
466
+ }
467
+ ```
468
+
469
+ Available errors:
470
+ - `AssetError` - Base error
471
+ - `AssetNotFoundError`
472
+ - `AssetExistsError`
473
+ - `AssetNotReissuableError`
474
+ - `InvalidAssetNameError`
475
+ - `InvalidAddressError`
476
+ - `InsufficientFundsError`
477
+ - `InsufficientAssetBalanceError`
478
+ - `OwnerTokenNotFoundError`
479
+ - `OwnerTokenNotReturnedError` (CRITICAL)
480
+ - `MaxSupplyExceededError`
481
+ - `InvalidIpfsHashError`
482
+ - `InvalidVerifierStringError`
483
+
484
+ ## Wallet Integration
485
+
486
+ ```javascript
487
+ const NeuraiWallet = require('@neuraiproject/neurai-jswallet');
488
+ const NeuraiAssets = require('@neuraiproject/neurai-assets');
489
+
490
+ // Initialize wallet
491
+ const wallet = new NeuraiWallet(mnemonic, {
492
+ network: 'xna',
493
+ rpcUrl: 'http://localhost:9766',
494
+ rpcUser: 'user',
495
+ rpcPassword: 'pass'
496
+ });
497
+
498
+ // Initialize assets with wallet RPC
499
+ const assets = new NeuraiAssets(
500
+ wallet.rpc.bind(wallet),
501
+ {
502
+ network: 'xna',
503
+ addresses: wallet.getAllAddresses(),
504
+ changeAddress: wallet.getChangeAddress(),
505
+ toAddress: wallet.getReceivingAddress()
506
+ }
507
+ );
508
+
509
+ // Create asset
510
+ const result = await assets.createRootAsset({
511
+ assetName: 'MYTOKEN',
512
+ quantity: 1000000,
513
+ units: 2
514
+ });
515
+
516
+ // Sign with wallet
517
+ const signedTx = await wallet.signTransaction(result.rawTx);
518
+
519
+ // Broadcast
520
+ const txid = await wallet.broadcastTransaction(signedTx);
521
+ console.log('Transaction ID:', txid);
522
+ ```
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Example: Create a ROOT Asset
3
+ *
4
+ * This example demonstrates how to create a ROOT asset (standard token).
5
+ * ROOT assets are the base asset type in Neurai.
6
+ *
7
+ * Cost: 1000 XNA (burned)
8
+ * Creates: MYTOKEN + MYTOKEN! (owner token)
9
+ */
10
+
11
+ const NeuraiAssets = require('@neuraiproject/neurai-assets');
12
+
13
+ async function createRootAsset() {
14
+ // Mock RPC function (replace with your actual RPC client)
15
+ const rpc = async (method, params) => {
16
+ console.log(`RPC Call: ${method}`, params);
17
+ // Your RPC implementation here
18
+ // return await yourRpcClient.call(method, params);
19
+ };
20
+
21
+ // Initialize NeuraiAssets
22
+ const assets = new NeuraiAssets(rpc, {
23
+ network: 'xna', // 'xna' for mainnet, 'xna-test' for testnet
24
+ addresses: ['NYourAddress1...'], // Your wallet addresses
25
+ changeAddress: 'NChangeAddress...', // Address to receive change
26
+ toAddress: 'NReceivingAddress...' // Address to receive the new asset
27
+ });
28
+
29
+ try {
30
+ // Create a ROOT asset
31
+ const result = await assets.createRootAsset({
32
+ assetName: 'MYTOKEN', // Asset name (3-30 chars, A-Z 0-9 _ .)
33
+ quantity: 1000000, // Total supply
34
+ units: 2, // Decimals (0-8), 2 means divisible by 0.01
35
+ reissuable: true, // Allow minting more supply later
36
+ hasIpfs: true, // Whether to include IPFS metadata
37
+ ipfsHash: 'QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG' // IPFS hash
38
+ });
39
+
40
+ console.log('Transaction created successfully!');
41
+ console.log('Raw Transaction (hex):', result.rawTx);
42
+ console.log('Fee:', result.fee, 'XNA');
43
+ console.log('Burn:', result.burn, 'XNA');
44
+ console.log('Owner Token:', result.metadata.ownerTokenName);
45
+
46
+ // Next steps:
47
+ // 1. Sign the raw transaction with your wallet
48
+ // 2. Broadcast the signed transaction to the network
49
+ // 3. Wait for confirmation
50
+
51
+ // Example with wallet integration:
52
+ // const signedTx = await wallet.signTransaction(result.rawTx);
53
+ // const txid = await wallet.broadcastTransaction(signedTx);
54
+ // console.log('Transaction ID:', txid);
55
+
56
+ } catch (error) {
57
+ console.error('Error creating ROOT asset:', error.message);
58
+
59
+ // Handle specific errors
60
+ if (error.name === 'AssetExistsError') {
61
+ console.error('This asset already exists on the blockchain');
62
+ } else if (error.name === 'InsufficientFundsError') {
63
+ console.error('Not enough XNA to pay for the transaction');
64
+ } else if (error.name === 'InvalidAssetNameError') {
65
+ console.error('Invalid asset name format');
66
+ }
67
+ }
68
+ }
69
+
70
+ // Run the example
71
+ createRootAsset();
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Example: Create a SUB Asset
3
+ *
4
+ * This example demonstrates how to create a SUB asset.
5
+ * SUB assets are child assets of a ROOT asset (format: ROOT/SUB).
6
+ *
7
+ * Requirements:
8
+ * - Must own the parent ROOT asset's owner token (PARENT!)
9
+ *
10
+ * Cost: 200 XNA (burned)
11
+ * Creates: PARENT/SUB + PARENT/SUB! (owner token)
12
+ */
13
+
14
+ const NeuraiAssets = require('@neuraiproject/neurai-assets');
15
+
16
+ async function createSubAsset() {
17
+ // Mock RPC function (replace with your actual RPC client)
18
+ const rpc = async (method, params) => {
19
+ console.log(`RPC Call: ${method}`, params);
20
+ // Your RPC implementation here
21
+ };
22
+
23
+ // Initialize NeuraiAssets
24
+ const assets = new NeuraiAssets(rpc, {
25
+ network: 'xna',
26
+ addresses: ['NYourAddress1...'],
27
+ changeAddress: 'NChangeAddress...',
28
+ toAddress: 'NReceivingAddress...'
29
+ });
30
+
31
+ try {
32
+ // First, verify you own the parent's owner token
33
+ const parentOwnerToken = 'MYTOKEN!';
34
+ const myAssets = await assets.listMyAssets(parentOwnerToken);
35
+
36
+ if (!myAssets[parentOwnerToken]) {
37
+ throw new Error(`You must own ${parentOwnerToken} to create a SUB asset`);
38
+ }
39
+
40
+ console.log(`✓ Owner token found: ${parentOwnerToken}`);
41
+
42
+ // Create a SUB asset
43
+ const result = await assets.createSubAsset({
44
+ assetName: 'MYTOKEN/PREMIUM', // Format: PARENT/SUB
45
+ quantity: 100000, // Total supply of SUB asset
46
+ units: 0, // Decimals (0-8)
47
+ reissuable: true, // Allow reissuance
48
+ hasIpfs: false, // No IPFS metadata in this example
49
+ ipfsHash: ''
50
+ });
51
+
52
+ console.log('SUB Asset transaction created successfully!');
53
+ console.log('Raw Transaction:', result.rawTx);
54
+ console.log('Fee:', result.fee, 'XNA');
55
+ console.log('Burn:', result.burn, 'XNA');
56
+ console.log('Owner Token Used:', result.metadata.ownerTokenUsed);
57
+ console.log('New Owner Token:', result.metadata.ownerTokenName);
58
+
59
+ // IMPORTANT: The parent's owner token (MYTOKEN!) is returned in the transaction
60
+ // and must not be lost. The library validates this automatically.
61
+
62
+ console.log('\nNext steps:');
63
+ console.log('1. Sign the transaction with your wallet');
64
+ console.log('2. Broadcast to the network');
65
+ console.log('3. You will receive MYTOKEN/PREMIUM and MYTOKEN/PREMIUM!');
66
+
67
+ } catch (error) {
68
+ console.error('Error creating SUB asset:', error.message);
69
+
70
+ if (error.name === 'OwnerTokenNotFoundError') {
71
+ console.error('You do not own the required owner token');
72
+ } else if (error.name === 'InvalidAssetNameError') {
73
+ console.error('Invalid SUB asset name format (must be PARENT/SUB)');
74
+ }
75
+ }
76
+ }
77
+
78
+ // Run the example
79
+ createSubAsset();