@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
|
@@ -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;
|