@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,135 @@
1
+ /**
2
+ * Asset-Specific Error Classes
3
+ * Errors related to asset operations
4
+ */
5
+
6
+ class AssetError extends Error {
7
+ constructor(message) {
8
+ super(message);
9
+ this.name = 'AssetError';
10
+ this.code = 'ASSET_ERROR';
11
+ }
12
+ }
13
+
14
+ class AssetExistsError extends AssetError {
15
+ constructor(message, assetName) {
16
+ super(message);
17
+ this.name = 'AssetExistsError';
18
+ this.code = 'ASSET_EXISTS';
19
+ this.assetName = assetName;
20
+ }
21
+ }
22
+
23
+ class AssetNotFoundError extends AssetError {
24
+ constructor(message, assetName) {
25
+ super(message);
26
+ this.name = 'AssetNotFoundError';
27
+ this.code = 'ASSET_NOT_FOUND';
28
+ this.assetName = assetName;
29
+ }
30
+ }
31
+
32
+ class OwnerTokenNotFoundError extends AssetError {
33
+ constructor(message, ownerTokenName) {
34
+ super(message);
35
+ this.name = 'OwnerTokenNotFoundError';
36
+ this.code = 'OWNER_TOKEN_NOT_FOUND';
37
+ this.ownerTokenName = ownerTokenName;
38
+ this.severity = 'HIGH';
39
+ }
40
+ }
41
+
42
+ class OwnerTokenNotReturnedError extends AssetError {
43
+ constructor(message, ownerTokenName) {
44
+ super(message);
45
+ this.name = 'OwnerTokenNotReturnedError';
46
+ this.code = 'OWNER_TOKEN_NOT_RETURNED';
47
+ this.ownerTokenName = ownerTokenName;
48
+ this.severity = 'CRITICAL'; // This results in permanent loss
49
+ }
50
+ }
51
+
52
+ class AssetNotReissuableError extends AssetError {
53
+ constructor(message, assetName) {
54
+ super(message);
55
+ this.name = 'AssetNotReissuableError';
56
+ this.code = 'ASSET_NOT_REISSUABLE';
57
+ this.assetName = assetName;
58
+ }
59
+ }
60
+
61
+ class InsufficientBurnAmountError extends AssetError {
62
+ constructor(message, required, provided) {
63
+ super(message);
64
+ this.name = 'InsufficientBurnAmountError';
65
+ this.code = 'INSUFFICIENT_BURN_AMOUNT';
66
+ this.required = required;
67
+ this.provided = provided;
68
+ }
69
+ }
70
+
71
+ class InvalidBurnAddressError extends AssetError {
72
+ constructor(message, expectedAddress, providedAddress) {
73
+ super(message);
74
+ this.name = 'InvalidBurnAddressError';
75
+ this.code = 'INVALID_BURN_ADDRESS';
76
+ this.expectedAddress = expectedAddress;
77
+ this.providedAddress = providedAddress;
78
+ }
79
+ }
80
+
81
+ class MaxSupplyExceededError extends AssetError {
82
+ constructor(message, assetName, currentSupply, additionalAmount, maxSupply) {
83
+ super(message);
84
+ this.name = 'MaxSupplyExceededError';
85
+ this.code = 'MAX_SUPPLY_EXCEEDED';
86
+ this.assetName = assetName;
87
+ this.currentSupply = currentSupply;
88
+ this.additionalAmount = additionalAmount;
89
+ this.maxSupply = maxSupply;
90
+ }
91
+ }
92
+
93
+ class RestrictedAssetViolationError extends AssetError {
94
+ constructor(message, assetName, address, reason) {
95
+ super(message);
96
+ this.name = 'RestrictedAssetViolationError';
97
+ this.code = 'RESTRICTED_ASSET_VIOLATION';
98
+ this.assetName = assetName;
99
+ this.address = address;
100
+ this.reason = reason;
101
+ }
102
+ }
103
+
104
+ class QualifierNotFoundError extends AssetError {
105
+ constructor(message, qualifierName) {
106
+ super(message);
107
+ this.name = 'QualifierNotFoundError';
108
+ this.code = 'QUALIFIER_NOT_FOUND';
109
+ this.qualifierName = qualifierName;
110
+ }
111
+ }
112
+
113
+ class ParentAssetNotFoundError extends AssetError {
114
+ constructor(message, parentAssetName) {
115
+ super(message);
116
+ this.name = 'ParentAssetNotFoundError';
117
+ this.code = 'PARENT_ASSET_NOT_FOUND';
118
+ this.parentAssetName = parentAssetName;
119
+ }
120
+ }
121
+
122
+ module.exports = {
123
+ AssetError,
124
+ AssetExistsError,
125
+ AssetNotFoundError,
126
+ OwnerTokenNotFoundError,
127
+ OwnerTokenNotReturnedError,
128
+ AssetNotReissuableError,
129
+ InsufficientBurnAmountError,
130
+ InvalidBurnAddressError,
131
+ MaxSupplyExceededError,
132
+ RestrictedAssetViolationError,
133
+ QualifierNotFoundError,
134
+ ParentAssetNotFoundError
135
+ };
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Validation Error Classes
3
+ * Errors thrown during parameter validation
4
+ */
5
+
6
+ class ValidationError extends Error {
7
+ constructor(message) {
8
+ super(message);
9
+ this.name = 'ValidationError';
10
+ this.code = 'VALIDATION_ERROR';
11
+ }
12
+ }
13
+
14
+ class InvalidAssetNameError extends ValidationError {
15
+ constructor(message, assetName) {
16
+ super(message);
17
+ this.name = 'InvalidAssetNameError';
18
+ this.code = 'INVALID_ASSET_NAME';
19
+ this.assetName = assetName;
20
+ }
21
+ }
22
+
23
+ class InvalidAmountError extends ValidationError {
24
+ constructor(message, amount) {
25
+ super(message);
26
+ this.name = 'InvalidAmountError';
27
+ this.code = 'INVALID_AMOUNT';
28
+ this.amount = amount;
29
+ }
30
+ }
31
+
32
+ class InvalidUnitsError extends ValidationError {
33
+ constructor(message, units) {
34
+ super(message);
35
+ this.name = 'InvalidUnitsError';
36
+ this.code = 'INVALID_UNITS';
37
+ this.units = units;
38
+ }
39
+ }
40
+
41
+ class InvalidVerifierStringError extends ValidationError {
42
+ constructor(message, verifier) {
43
+ super(message);
44
+ this.name = 'InvalidVerifierStringError';
45
+ this.code = 'INVALID_VERIFIER_STRING';
46
+ this.verifier = verifier;
47
+ }
48
+ }
49
+
50
+ class InvalidIPFSHashError extends ValidationError {
51
+ constructor(message, hash) {
52
+ super(message);
53
+ this.name = 'InvalidIPFSHashError';
54
+ this.code = 'INVALID_IPFS_HASH';
55
+ this.ipfsHash = hash;
56
+ }
57
+ }
58
+
59
+ class InvalidAddressError extends ValidationError {
60
+ constructor(message, address) {
61
+ super(message);
62
+ this.name = 'InvalidAddressError';
63
+ this.code = 'INVALID_ADDRESS';
64
+ this.address = address;
65
+ }
66
+ }
67
+
68
+ class InsufficientFundsError extends ValidationError {
69
+ constructor(message, required, available) {
70
+ super(message);
71
+ this.name = 'InsufficientFundsError';
72
+ this.code = 'INSUFFICIENT_FUNDS';
73
+ this.required = required;
74
+ this.available = available;
75
+ }
76
+ }
77
+
78
+ module.exports = {
79
+ ValidationError,
80
+ InvalidAssetNameError,
81
+ InvalidAmountError,
82
+ InvalidUnitsError,
83
+ InvalidVerifierStringError,
84
+ InvalidIPFSHashError,
85
+ InvalidAddressError,
86
+ InsufficientFundsError
87
+ };
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Errors Module
3
+ * Exports all error classes
4
+ */
5
+
6
+ const {
7
+ ValidationError,
8
+ InvalidAssetNameError,
9
+ InvalidAmountError,
10
+ InvalidUnitsError,
11
+ InvalidVerifierStringError,
12
+ InvalidIPFSHashError,
13
+ InvalidAddressError,
14
+ InsufficientFundsError
15
+ } = require('./ValidationErrors');
16
+
17
+ const {
18
+ AssetError,
19
+ AssetExistsError,
20
+ AssetNotFoundError,
21
+ OwnerTokenNotFoundError,
22
+ OwnerTokenNotReturnedError,
23
+ AssetNotReissuableError,
24
+ InsufficientBurnAmountError,
25
+ InvalidBurnAddressError,
26
+ MaxSupplyExceededError,
27
+ RestrictedAssetViolationError,
28
+ QualifierNotFoundError,
29
+ ParentAssetNotFoundError
30
+ } = require('./AssetErrors');
31
+
32
+ module.exports = {
33
+ // Validation Errors
34
+ ValidationError,
35
+ InvalidAssetNameError,
36
+ InvalidAmountError,
37
+ InvalidUnitsError,
38
+ InvalidVerifierStringError,
39
+ InvalidIPFSHashError,
40
+ InvalidAddressError,
41
+ InsufficientFundsError,
42
+
43
+ // Asset Errors
44
+ AssetError,
45
+ AssetExistsError,
46
+ AssetNotFoundError,
47
+ OwnerTokenNotFoundError,
48
+ OwnerTokenNotReturnedError,
49
+ AssetNotReissuableError,
50
+ InsufficientBurnAmountError,
51
+ InvalidBurnAddressError,
52
+ MaxSupplyExceededError,
53
+ RestrictedAssetViolationError,
54
+ QualifierNotFoundError,
55
+ ParentAssetNotFoundError
56
+ };
package/src/index.js ADDED
@@ -0,0 +1,68 @@
1
+ /**
2
+ * @neuraiproject/neurai-assets
3
+ * Non-custodial Neurai asset management library
4
+ *
5
+ * Comprehensive asset management library for Neurai blockchain
6
+ * Supports creation, reissuance, transfers, and queries for all asset types:
7
+ * - ROOT assets (standard tokens)
8
+ * - SUB assets (sub-tokens)
9
+ * - UNIQUE assets (NFTs)
10
+ * - QUALIFIER assets (KYC/compliance tags)
11
+ * - RESTRICTED assets (security tokens with compliance)
12
+ *
13
+ * @example
14
+ * const NeuraiAssets = require('@neuraiproject/neurai-assets');
15
+ *
16
+ * // Initialize with RPC function
17
+ * const assets = new NeuraiAssets(rpc, {
18
+ * network: 'xna',
19
+ * addresses: walletAddresses,
20
+ * changeAddress: myChangeAddress,
21
+ * toAddress: myReceivingAddress
22
+ * });
23
+ *
24
+ * // Create a ROOT asset
25
+ * const result = await assets.createRootAsset({
26
+ * assetName: 'MYTOKEN',
27
+ * quantity: 1000000,
28
+ * units: 2,
29
+ * reissuable: true
30
+ * });
31
+ *
32
+ * // Sign and broadcast
33
+ * const signedTx = await wallet.signTransaction(result.rawTx);
34
+ * const txid = await wallet.broadcastTransaction(signedTx);
35
+ */
36
+
37
+ // Main API class
38
+ const NeuraiAssets = require('./NeuraiAssets');
39
+
40
+ // Builders
41
+ const builders = require('./builders');
42
+
43
+ // Queries
44
+ const { AssetQueries } = require('./queries');
45
+
46
+ // Constants
47
+ const constants = require('./constants');
48
+
49
+ // Errors
50
+ const errors = require('./errors');
51
+
52
+ // Validators
53
+ const validators = require('./validators');
54
+
55
+ // Utils
56
+ const utils = require('./utils');
57
+
58
+ // Export main class as default
59
+ module.exports = NeuraiAssets;
60
+
61
+ // Export everything as named exports
62
+ module.exports.NeuraiAssets = NeuraiAssets;
63
+ module.exports.AssetQueries = AssetQueries;
64
+ module.exports.builders = builders;
65
+ module.exports.constants = constants;
66
+ module.exports.errors = errors;
67
+ module.exports.validators = validators;
68
+ module.exports.utils = utils;
@@ -0,0 +1,222 @@
1
+ /**
2
+ * Burn Manager
3
+ * Manages burn addresses and amounts for asset operations
4
+ *
5
+ * Each asset operation requires burning XNA to specific addresses.
6
+ * This manager ensures correct burn addresses and amounts are used
7
+ * based on network (mainnet/testnet) and operation type.
8
+ */
9
+
10
+ const { getBurnAddress, getAssetCost } = require('../constants');
11
+ const { InsufficientBurnAmountError, InvalidBurnAddressError } = require('../errors');
12
+
13
+ class BurnManager {
14
+ /**
15
+ * @param {string} network - Network type ('xna' or 'xna-test')
16
+ */
17
+ constructor(network) {
18
+ if (!network) {
19
+ throw new Error('Network is required');
20
+ }
21
+ this.network = network;
22
+ }
23
+
24
+ /**
25
+ * Get burn address for an operation
26
+ * @param {string} operationType - Operation type (e.g., 'ISSUE_ROOT')
27
+ * @returns {string} Burn address for the network
28
+ */
29
+ getBurnAddress(operationType) {
30
+ return getBurnAddress(operationType, this.network);
31
+ }
32
+
33
+ /**
34
+ * Get burn amount for an operation
35
+ * @param {string} operationType - Operation type (e.g., 'ISSUE_ROOT')
36
+ * @param {number} multiplier - Multiplier for operations like UNIQUE (default: 1)
37
+ * @returns {number} Burn amount in XNA
38
+ */
39
+ getBurnAmount(operationType, multiplier = 1) {
40
+ const baseCost = getAssetCost(operationType);
41
+ return baseCost * multiplier;
42
+ }
43
+
44
+ /**
45
+ * Get burn info (address + amount) for an operation
46
+ * @param {string} operationType - Operation type
47
+ * @param {number} multiplier - Multiplier (default: 1)
48
+ * @returns {object} { address, amount }
49
+ */
50
+ getBurnInfo(operationType, multiplier = 1) {
51
+ return {
52
+ address: this.getBurnAddress(operationType),
53
+ amount: this.getBurnAmount(operationType, multiplier)
54
+ };
55
+ }
56
+
57
+ /**
58
+ * Get burn info for ROOT asset issuance
59
+ * @returns {object} { address, amount }
60
+ */
61
+ getIssueRootBurn() {
62
+ return this.getBurnInfo('ISSUE_ROOT');
63
+ }
64
+
65
+ /**
66
+ * Get burn info for SUB asset issuance
67
+ * @returns {object} { address, amount }
68
+ */
69
+ getIssueSubBurn() {
70
+ return this.getBurnInfo('ISSUE_SUB');
71
+ }
72
+
73
+ /**
74
+ * Get burn info for UNIQUE asset issuance
75
+ * @param {number} count - Number of unique assets to create
76
+ * @returns {object} { address, amount }
77
+ */
78
+ getIssueUniqueBurn(count) {
79
+ return this.getBurnInfo('ISSUE_UNIQUE', count);
80
+ }
81
+
82
+ /**
83
+ * Get burn info for QUALIFIER asset issuance
84
+ * @returns {object} { address, amount }
85
+ */
86
+ getIssueQualifierBurn() {
87
+ return this.getBurnInfo('ISSUE_QUALIFIER');
88
+ }
89
+
90
+ /**
91
+ * Get burn info for SUB_QUALIFIER asset issuance
92
+ * @returns {object} { address, amount }
93
+ */
94
+ getIssueSubQualifierBurn() {
95
+ return this.getBurnInfo('ISSUE_SUB_QUALIFIER');
96
+ }
97
+
98
+ /**
99
+ * Get burn info for RESTRICTED asset issuance
100
+ * @returns {object} { address, amount }
101
+ */
102
+ getIssueRestrictedBurn() {
103
+ return this.getBurnInfo('ISSUE_RESTRICTED');
104
+ }
105
+
106
+ /**
107
+ * Get burn info for REISSUE operation
108
+ * @returns {object} { address, amount }
109
+ */
110
+ getReissueBurn() {
111
+ return this.getBurnInfo('REISSUE');
112
+ }
113
+
114
+ /**
115
+ * Get burn info for TAG_ADDRESS operation
116
+ * @param {number} addressCount - Number of addresses to tag
117
+ * @returns {object} { address, amount }
118
+ */
119
+ getTagAddressBurn(addressCount) {
120
+ return this.getBurnInfo('TAG_ADDRESS', addressCount);
121
+ }
122
+
123
+ /**
124
+ * Get burn info for UNTAG_ADDRESS operation
125
+ * @param {number} addressCount - Number of addresses to untag
126
+ * @returns {object} { address, amount }
127
+ */
128
+ getUntagAddressBurn(addressCount) {
129
+ return this.getBurnInfo('UNTAG_ADDRESS', addressCount);
130
+ }
131
+
132
+ /**
133
+ * Validate that burn output is correct
134
+ * @param {object} outputs - Transaction outputs
135
+ * @param {string} operationType - Expected operation type
136
+ * @param {number} multiplier - Expected multiplier (default: 1)
137
+ * @returns {boolean} True if valid
138
+ * @throws {Error} If burn is invalid
139
+ */
140
+ validateBurnOutput(outputs, operationType, multiplier = 1) {
141
+ const expectedBurn = this.getBurnInfo(operationType, multiplier);
142
+
143
+ // Check if burn address exists in outputs
144
+ const burnAmount = outputs[expectedBurn.address];
145
+ if (burnAmount === undefined) {
146
+ throw new InvalidBurnAddressError(
147
+ `Expected burn to ${expectedBurn.address} not found in outputs`,
148
+ expectedBurn.address,
149
+ null
150
+ );
151
+ }
152
+
153
+ // Check if burn amount is correct
154
+ if (burnAmount !== expectedBurn.amount) {
155
+ throw new InsufficientBurnAmountError(
156
+ `Incorrect burn amount. Expected ${expectedBurn.amount} XNA, got ${burnAmount} XNA`,
157
+ expectedBurn.amount,
158
+ burnAmount
159
+ );
160
+ }
161
+
162
+ return true;
163
+ }
164
+
165
+ /**
166
+ * Check if an address is a burn address for this network
167
+ * @param {string} address - Address to check
168
+ * @returns {boolean} True if it's a burn address
169
+ */
170
+ isBurnAddress(address) {
171
+ const burnTypes = [
172
+ 'ISSUE_ROOT',
173
+ 'ISSUE_SUB',
174
+ 'ISSUE_UNIQUE',
175
+ 'ISSUE_MSGCHANNEL',
176
+ 'REISSUE',
177
+ 'ISSUE_RESTRICTED',
178
+ 'ISSUE_QUALIFIER',
179
+ 'ISSUE_SUB_QUALIFIER',
180
+ 'TAG_ADDRESS',
181
+ 'UNTAG_ADDRESS'
182
+ ];
183
+
184
+ for (const type of burnTypes) {
185
+ if (this.getBurnAddress(type) === address) {
186
+ return true;
187
+ }
188
+ }
189
+
190
+ return false;
191
+ }
192
+
193
+ /**
194
+ * Get operation type from burn address
195
+ * @param {string} address - Burn address
196
+ * @returns {string|null} Operation type or null if not a burn address
197
+ */
198
+ getOperationTypeFromBurnAddress(address) {
199
+ const burnTypes = [
200
+ 'ISSUE_ROOT',
201
+ 'ISSUE_SUB',
202
+ 'ISSUE_UNIQUE',
203
+ 'ISSUE_MSGCHANNEL',
204
+ 'REISSUE',
205
+ 'ISSUE_RESTRICTED',
206
+ 'ISSUE_QUALIFIER',
207
+ 'ISSUE_SUB_QUALIFIER',
208
+ 'TAG_ADDRESS',
209
+ 'UNTAG_ADDRESS'
210
+ ];
211
+
212
+ for (const type of burnTypes) {
213
+ if (this.getBurnAddress(type) === address) {
214
+ return type;
215
+ }
216
+ }
217
+
218
+ return null;
219
+ }
220
+ }
221
+
222
+ module.exports = BurnManager;