@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.
- package/README.md +522 -0
- package/examples/01-create-root-asset.js +71 -0
- package/examples/02-create-sub-asset.js +79 -0
- package/examples/03-create-nfts.js +140 -0
- package/examples/04-reissue-asset.js +164 -0
- package/examples/05-create-qualifier-and-tag.js +209 -0
- package/examples/06-create-restricted-asset.js +223 -0
- package/examples/07-freeze-and-unfreeze.js +292 -0
- package/examples/08-query-assets.js +332 -0
- package/examples/09-wallet-integration.js +320 -0
- package/examples/README.md +319 -0
- package/package.json +43 -0
- package/src/NeuraiAssets.js +468 -0
- package/src/builders/BaseAssetTransactionBuilder.js +303 -0
- package/src/builders/FreezeAddressBuilder.js +271 -0
- package/src/builders/IssueQualifierBuilder.js +251 -0
- package/src/builders/IssueRestrictedBuilder.js +187 -0
- package/src/builders/IssueRootBuilder.js +173 -0
- package/src/builders/IssueSubBuilder.js +237 -0
- package/src/builders/IssueUniqueBuilder.js +255 -0
- package/src/builders/ReissueBuilder.js +246 -0
- package/src/builders/ReissueRestrictedBuilder.js +264 -0
- package/src/builders/TagAddressBuilder.js +243 -0
- package/src/builders/index.js +38 -0
- package/src/constants/assetTypes.js +23 -0
- package/src/constants/burnAddresses.js +65 -0
- package/src/constants/fees.js +61 -0
- package/src/constants/index.js +44 -0
- package/src/constants/networks.js +112 -0
- package/src/errors/AssetErrors.js +135 -0
- package/src/errors/ValidationErrors.js +87 -0
- package/src/errors/index.js +56 -0
- package/src/index.js +68 -0
- package/src/managers/BurnManager.js +222 -0
- package/src/managers/OutputOrderer.js +289 -0
- package/src/managers/OwnerTokenManager.js +265 -0
- package/src/managers/UTXOSelector.js +309 -0
- package/src/managers/index.js +16 -0
- package/src/queries/AssetQueries.js +447 -0
- package/src/queries/index.js +10 -0
- package/src/utils/amountConverter.js +115 -0
- package/src/utils/assetNameParser.js +203 -0
- package/src/utils/index.js +16 -0
- package/src/utils/networkDetector.js +144 -0
- package/src/utils/outputFormatter.js +292 -0
- package/src/validators/amountValidator.js +149 -0
- package/src/validators/assetNameValidator.js +296 -0
- package/src/validators/index.js +16 -0
- package/src/validators/ipfsValidator.js +101 -0
- package/src/validators/verifierValidator.js +146 -0
- package/tests/README.md +126 -0
- package/tests/integration/assetLifecycle.test.js +244 -0
- package/tests/mocks/rpcMock.js +156 -0
- package/tests/unit/NeuraiAssets.test.js +217 -0
- package/tests/unit/utils/amountConverter.test.js +171 -0
- package/tests/unit/utils/assetNameParser.test.js +203 -0
- package/tests/unit/validators/amountValidator.test.js +143 -0
- 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();
|