@neuraiproject/neurai-assets 1.2.0 → 1.2.2

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/dist/index.js CHANGED
@@ -4169,7 +4169,7 @@ function requireBaseAssetTransactionBuilder () {
4169
4169
  addresses === undefined
4170
4170
  ) {
4171
4171
  network = networkOrParams.network;
4172
- actualAddresses = networkOrParams.addresses;
4172
+ actualAddresses = networkOrParams.walletAddresses || networkOrParams.addresses;
4173
4173
  actualParams = networkOrParams;
4174
4174
  } else {
4175
4175
  network = networkOrParams;
@@ -4373,23 +4373,163 @@ function requireBaseAssetTransactionBuilder () {
4373
4373
  }
4374
4374
 
4375
4375
  /**
4376
- * Convert amount to satoshis
4377
- * @param {number} amount - Amount in asset units
4378
- * @param {number} units - Decimal places
4379
- * @returns {number} Amount in satoshis
4376
+ * Convert asset amount to protocol raw units.
4377
+ * Asset raw quantities in transaction payloads are always encoded with
4378
+ * 8 decimal places, regardless of the asset's displayed `units`.
4379
+ *
4380
+ * @param {number} amount - User-facing asset amount
4381
+ * @param {number} units - Asset decimal places (kept for API compatibility)
4382
+ * @returns {number} Amount in protocol raw units
4380
4383
  */
4381
4384
  toSatoshis(amount, units) {
4382
- return Math.round(amount * Math.pow(10, units));
4385
+ return Math.round(amount * 100000000);
4383
4386
  }
4384
4387
 
4385
4388
  /**
4386
- * Convert satoshis to amount
4387
- * @param {number} satoshis - Amount in satoshis
4388
- * @param {number} units - Decimal places
4389
+ * Convert protocol raw units back to a user-facing asset amount.
4390
+ *
4391
+ * @param {number} satoshis - Amount in protocol raw units
4392
+ * @param {number} units - Asset decimal places (kept for API compatibility)
4389
4393
  * @returns {number} Amount in asset units
4390
4394
  */
4391
4395
  fromSatoshis(satoshis, units) {
4392
- return satoshis / Math.pow(10, units);
4396
+ return satoshis / 100000000;
4397
+ }
4398
+
4399
+ /**
4400
+ * Normalize builder inputs to raw transaction inputs
4401
+ * @param {Array} inputs - Builder inputs
4402
+ * @returns {Array<{txid: string, vout: number}>} Raw transaction inputs
4403
+ */
4404
+ toRawTxInputs(inputs) {
4405
+ return (inputs || []).map(input => ({
4406
+ txid: input.txid,
4407
+ vout: input.vout !== undefined ? input.vout : input.outputIndex
4408
+ }));
4409
+ }
4410
+
4411
+ /**
4412
+ * Convert XNA amount to satoshis
4413
+ * @param {number|null|undefined} amount - Amount in XNA
4414
+ * @returns {number|undefined} Amount in satoshis
4415
+ */
4416
+ xnaToSatoshis(amount) {
4417
+ if (amount === undefined || amount === null) {
4418
+ return undefined;
4419
+ }
4420
+
4421
+ return Math.round(amount * 100000000);
4422
+ }
4423
+
4424
+ /**
4425
+ * Build a typed local raw build payload compatible with
4426
+ * @neuraiproject/neurai-create-transaction createFromOperation(...)
4427
+ *
4428
+ * @param {string} operationType - Operation type
4429
+ * @param {Array} inputs - Builder inputs
4430
+ * @param {object|null} burnInfo - Burn metadata
4431
+ * @param {string|null} changeAddress - XNA change address
4432
+ * @param {number|null} changeAmount - XNA change amount in XNA
4433
+ * @param {object} operationParams - Operation-specific params
4434
+ * @returns {{ operationType: string, params: object }} Local raw build
4435
+ */
4436
+ buildLocalRawBuild(
4437
+ operationType,
4438
+ inputs,
4439
+ burnInfo = null,
4440
+ changeAddress = null,
4441
+ changeAmount = null,
4442
+ operationParams = {}
4443
+ ) {
4444
+ const params = {
4445
+ inputs: this.toRawTxInputs(inputs),
4446
+ ...operationParams
4447
+ };
4448
+
4449
+ if (burnInfo && burnInfo.address && burnInfo.amount !== undefined && burnInfo.amount !== null) {
4450
+ params.burnAddress = burnInfo.address;
4451
+ params.burnAmountSats = this.xnaToSatoshis(burnInfo.amount);
4452
+ }
4453
+
4454
+ if (changeAddress && changeAmount !== undefined && changeAmount !== null) {
4455
+ params.xnaChangeAddress = changeAddress;
4456
+ params.xnaChangeSats = this.xnaToSatoshis(changeAmount);
4457
+ }
4458
+
4459
+ return {
4460
+ operationType,
4461
+ params
4462
+ };
4463
+ }
4464
+
4465
+ /**
4466
+ * Normalize ordered outputs into flat entries
4467
+ * @param {Array|object} outputs - Transaction outputs
4468
+ * @returns {Array<{address: string, value: unknown}>} Output entries
4469
+ */
4470
+ getOutputEntries(outputs) {
4471
+ if (Array.isArray(outputs)) {
4472
+ return outputs.map(output => {
4473
+ const [address, value] = Object.entries(output)[0];
4474
+ return { address, value };
4475
+ });
4476
+ }
4477
+
4478
+ if (outputs && typeof outputs === 'object') {
4479
+ return Object.entries(outputs).map(([address, value]) => ({ address, value }));
4480
+ }
4481
+
4482
+ return [];
4483
+ }
4484
+
4485
+ /**
4486
+ * Extract burn metadata from outputs
4487
+ * @param {Array<{address: string, value: unknown}>} entries - Output entries
4488
+ * @param {number} burnAmount - Burn amount in XNA
4489
+ * @returns {{ burnAddress: string|null, burnAmount: number }}
4490
+ */
4491
+ extractBurnMetadata(entries, burnAmount) {
4492
+ if (!burnAmount || burnAmount <= 0) {
4493
+ return {
4494
+ burnAddress: null,
4495
+ burnAmount: 0
4496
+ };
4497
+ }
4498
+
4499
+ const burnEntry = entries.find(({ address, value }) => {
4500
+ return typeof value === 'number' &&
4501
+ value === burnAmount &&
4502
+ this.burnManager.isBurnAddress(address);
4503
+ });
4504
+
4505
+ return {
4506
+ burnAddress: burnEntry ? burnEntry.address : null,
4507
+ burnAmount
4508
+ };
4509
+ }
4510
+
4511
+ /**
4512
+ * Extract XNA change metadata from outputs
4513
+ * @param {Array<{address: string, value: unknown}>} entries - Output entries
4514
+ * @param {string|null} burnAddress - Burn address if present
4515
+ * @returns {{ changeAddress: string|null, changeAmount: number|null }}
4516
+ */
4517
+ extractChangeMetadata(entries, burnAddress = null) {
4518
+ const xnaOutputs = entries.filter(({ address, value }) => {
4519
+ return typeof value === 'number' && address !== burnAddress;
4520
+ });
4521
+
4522
+ if (xnaOutputs.length !== 1) {
4523
+ return {
4524
+ changeAddress: null,
4525
+ changeAmount: null
4526
+ };
4527
+ }
4528
+
4529
+ return {
4530
+ changeAddress: xnaOutputs[0].address,
4531
+ changeAmount: xnaOutputs[0].value
4532
+ };
4393
4533
  }
4394
4534
 
4395
4535
  /**
@@ -4404,13 +4544,26 @@ function requireBaseAssetTransactionBuilder () {
4404
4544
  * @returns {object} Formatted result
4405
4545
  */
4406
4546
  formatResult(rawTx, utxos, inputs, outputs, fee, burnAmount, extra = {}) {
4547
+ const outputEntries = this.getOutputEntries(outputs);
4548
+ const burnMetadata = this.extractBurnMetadata(outputEntries, burnAmount);
4549
+ const changeMetadata = this.extractChangeMetadata(outputEntries, burnMetadata.burnAddress);
4550
+
4407
4551
  return {
4408
4552
  rawTx,
4409
4553
  utxos,
4410
4554
  inputs,
4411
4555
  outputs,
4412
4556
  fee,
4413
- burnAmount,
4557
+ burnAmount: burnMetadata.burnAmount,
4558
+ network: this.network,
4559
+ buildStrategy: 'rpc-node',
4560
+ burnAddress: burnMetadata.burnAddress,
4561
+ changeAddress: extra.changeAddress !== undefined
4562
+ ? extra.changeAddress
4563
+ : changeMetadata.changeAddress,
4564
+ changeAmount: extra.changeAmount !== undefined
4565
+ ? extra.changeAmount
4566
+ : changeMetadata.changeAmount,
4414
4567
  ...extra
4415
4568
  };
4416
4569
  }
@@ -4627,7 +4780,23 @@ function requireIssueRootBuilder () {
4627
4780
  {
4628
4781
  assetName,
4629
4782
  ownerTokenName: assetName + '!',
4630
- operationType: 'ISSUE_ROOT'
4783
+ operationType: 'ISSUE_ROOT',
4784
+ localRawBuild: this.buildLocalRawBuild(
4785
+ 'ISSUE_ROOT',
4786
+ inputs,
4787
+ burnInfo,
4788
+ changeAddress,
4789
+ xnaChange > 0.00000001 ? parseFloat(xnaChange.toFixed(8)) : null,
4790
+ {
4791
+ toAddress,
4792
+ assetName,
4793
+ quantityRaw: this.toSatoshis(quantity, units),
4794
+ units,
4795
+ reissuable,
4796
+ ipfsHash: hasIpfs ? ipfsHash : undefined,
4797
+ ownerTokenAddress: changeAddress
4798
+ }
4799
+ )
4631
4800
  }
4632
4801
  );
4633
4802
  }
@@ -4873,7 +5042,23 @@ function requireIssueSubBuilder () {
4873
5042
  parentAssetName,
4874
5043
  ownerTokenName: assetName + '!',
4875
5044
  parentOwnerTokenUsed: ownerTokenName,
4876
- operationType: 'ISSUE_SUB'
5045
+ operationType: 'ISSUE_SUB',
5046
+ localRawBuild: this.buildLocalRawBuild(
5047
+ 'ISSUE_SUB',
5048
+ inputs,
5049
+ burnInfo,
5050
+ changeAddress,
5051
+ xnaChange > 0.00000001 ? parseFloat(xnaChange.toFixed(8)) : null,
5052
+ {
5053
+ toAddress,
5054
+ assetName,
5055
+ quantityRaw: this.toSatoshis(quantity, units),
5056
+ units,
5057
+ reissuable,
5058
+ ipfsHash: hasIpfs ? ipfsHash : undefined,
5059
+ parentOwnerAddress: changeAddress
5060
+ }
5061
+ )
4877
5062
  }
4878
5063
  );
4879
5064
  }
@@ -5026,7 +5211,22 @@ function requireIssueDepinBuilder () {
5026
5211
  {
5027
5212
  assetName,
5028
5213
  ownerTokenName: `${assetName}!`,
5029
- operationType: 'ISSUE_DEPIN'
5214
+ operationType: 'ISSUE_DEPIN',
5215
+ localRawBuild: this.buildLocalRawBuild(
5216
+ 'ISSUE_DEPIN',
5217
+ inputs,
5218
+ burnInfo,
5219
+ changeAddress,
5220
+ xnaChange > 0.00000001 ? parseFloat(xnaChange.toFixed(8)) : null,
5221
+ {
5222
+ toAddress,
5223
+ assetName,
5224
+ quantityRaw: this.toSatoshis(quantity, 0),
5225
+ ipfsHash: hasIpfs ? ipfsHash : undefined,
5226
+ ownerTokenAddress: changeAddress,
5227
+ reissuable
5228
+ }
5229
+ )
5030
5230
  }
5031
5231
  );
5032
5232
  }
@@ -5274,7 +5474,23 @@ function requireReissueBuilder () {
5274
5474
  newTotalSupply,
5275
5475
  previousSupply: currentSupply,
5276
5476
  reissuableLocked: reissuable === false,
5277
- operationType: 'REISSUE'
5477
+ operationType: 'REISSUE',
5478
+ localRawBuild: this.buildLocalRawBuild(
5479
+ 'REISSUE',
5480
+ inputs,
5481
+ burnInfo,
5482
+ changeAddress,
5483
+ xnaChange > 0.00000001 ? parseFloat(xnaChange.toFixed(8)) : null,
5484
+ {
5485
+ toAddress,
5486
+ assetName,
5487
+ quantityRaw: this.toSatoshis(quantity, units),
5488
+ units,
5489
+ reissuable: reissuable !== undefined ? reissuable : undefined,
5490
+ ipfsHash: newIpfs || undefined,
5491
+ ownerChangeAddress: isDepinAsset ? toAddress : changeAddress
5492
+ }
5493
+ )
5278
5494
  }
5279
5495
  );
5280
5496
  }
@@ -5531,7 +5747,21 @@ function requireIssueUniqueBuilder () {
5531
5747
  createdNFTs,
5532
5748
  nftCount,
5533
5749
  ownerTokenUsed: ownerTokenName,
5534
- operationType: 'ISSUE_UNIQUE'
5750
+ operationType: 'ISSUE_UNIQUE',
5751
+ localRawBuild: this.buildLocalRawBuild(
5752
+ 'ISSUE_UNIQUE',
5753
+ inputs,
5754
+ burnInfo,
5755
+ changeAddress,
5756
+ xnaChange > 0.00000001 ? parseFloat(xnaChange.toFixed(8)) : null,
5757
+ {
5758
+ toAddress,
5759
+ rootName,
5760
+ assetTags,
5761
+ ipfsHashes: ipfsHashes.length > 0 ? ipfsHashes : undefined,
5762
+ ownerTokenAddress: changeAddress
5763
+ }
5764
+ )
5535
5765
  }
5536
5766
  );
5537
5767
  }
@@ -5746,11 +5976,13 @@ function requireIssueQualifierBuilder () {
5746
5976
  // Last: Issue qualifier operation
5747
5977
  const issueQualifierOutput = OutputFormatter.formatIssueQualifierOutput({
5748
5978
  asset_name: assetName,
5749
- asset_quantity: quantity,
5979
+ asset_quantity: this.toSatoshis(quantity, 0),
5750
5980
  has_ipfs: hasIpfs,
5751
5981
  ipfs_hash: ipfsHash,
5752
5982
  root_change_address: isSub ? changeAddress : undefined,
5753
- change_quantity: isSub ? parentQualifierQuantity : undefined
5983
+ change_quantity: isSub && parentQualifierQuantity !== null
5984
+ ? this.toSatoshis(parentQualifierQuantity, 0)
5985
+ : undefined
5754
5986
  });
5755
5987
 
5756
5988
  outputs.push({ [toAddress]: issueQualifierOutput });
@@ -5776,7 +6008,24 @@ function requireIssueQualifierBuilder () {
5776
6008
  qualifierType: isSub ? 'SUB_QUALIFIER' : 'QUALIFIER',
5777
6009
  parentQualifier: isSub ? parsed.parent : null,
5778
6010
  parentQualifierUsed: parentQualifierName,
5779
- operationType: isSub ? 'ISSUE_SUB_QUALIFIER' : 'ISSUE_QUALIFIER'
6011
+ operationType: isSub ? 'ISSUE_SUB_QUALIFIER' : 'ISSUE_QUALIFIER',
6012
+ localRawBuild: this.buildLocalRawBuild(
6013
+ isSub ? 'ISSUE_SUB_QUALIFIER' : 'ISSUE_QUALIFIER',
6014
+ inputs,
6015
+ burnInfo,
6016
+ changeAddress,
6017
+ xnaChange > 0.00000001 ? parseFloat(xnaChange.toFixed(8)) : null,
6018
+ {
6019
+ toAddress,
6020
+ assetName,
6021
+ quantityRaw: this.toSatoshis(quantity, 0),
6022
+ ipfsHash: hasIpfs ? ipfsHash : undefined,
6023
+ rootChangeAddress: isSub ? changeAddress : undefined,
6024
+ changeQuantityRaw: isSub && parentQualifierQuantity !== null
6025
+ ? this.toSatoshis(parentQualifierQuantity, 0)
6026
+ : undefined
6027
+ }
6028
+ )
5780
6029
  }
5781
6030
  );
5782
6031
  }
@@ -6006,7 +6255,24 @@ function requireIssueRestrictedBuilder () {
6006
6255
  ownerTokenName,
6007
6256
  verifierString,
6008
6257
  requiredQualifiers,
6009
- operationType: 'ISSUE_RESTRICTED'
6258
+ operationType: 'ISSUE_RESTRICTED',
6259
+ localRawBuild: this.buildLocalRawBuild(
6260
+ 'ISSUE_RESTRICTED',
6261
+ inputs,
6262
+ burnInfo,
6263
+ changeAddress,
6264
+ xnaChange > 0.00000001 ? parseFloat(xnaChange.toFixed(8)) : null,
6265
+ {
6266
+ toAddress,
6267
+ assetName,
6268
+ quantityRaw: this.toSatoshis(quantity, units),
6269
+ verifierString,
6270
+ units,
6271
+ reissuable,
6272
+ ipfsHash: hasIpfs ? ipfsHash : undefined,
6273
+ ownerChangeAddress: changeAddress
6274
+ }
6275
+ )
6010
6276
  }
6011
6277
  );
6012
6278
  }
@@ -6269,7 +6535,24 @@ function requireReissueRestrictedBuilder () {
6269
6535
  newVerifier: changeVerifier ? newVerifier : undefined,
6270
6536
  requiredQualifiers,
6271
6537
  reissuableLocked: reissuable === false,
6272
- operationType: 'REISSUE_RESTRICTED'
6538
+ operationType: 'REISSUE_RESTRICTED',
6539
+ localRawBuild: this.buildLocalRawBuild(
6540
+ 'REISSUE_RESTRICTED',
6541
+ inputs,
6542
+ burnInfo,
6543
+ changeAddress,
6544
+ xnaChange > 0.00000001 ? parseFloat(xnaChange.toFixed(8)) : null,
6545
+ {
6546
+ toAddress,
6547
+ assetName,
6548
+ quantityRaw: this.toSatoshis(quantity, assetData.units || 0),
6549
+ units: assetData.units || 0,
6550
+ reissuable: reissuable !== undefined ? reissuable : undefined,
6551
+ ipfsHash: newIpfs || undefined,
6552
+ ownerChangeAddress: this.params.ownerChangeAddress || changeAddress,
6553
+ verifierString: changeVerifier ? newVerifier : undefined
6554
+ }
6555
+ )
6273
6556
  }
6274
6557
  );
6275
6558
  }
@@ -6461,12 +6744,12 @@ function requireTagAddressBuilder () {
6461
6744
  ? OutputFormatter.formatUntagAddressesOutput({
6462
6745
  qualifier: qualifierName,
6463
6746
  addresses: targetAddresses,
6464
- change_quantity: qualifierQuantity
6747
+ change_quantity: this.toSatoshis(qualifierQuantity, 0)
6465
6748
  })
6466
6749
  : OutputFormatter.formatTagAddressesOutput({
6467
6750
  qualifier: qualifierName,
6468
6751
  addresses: targetAddresses,
6469
- change_quantity: qualifierQuantity
6752
+ change_quantity: this.toSatoshis(qualifierQuantity, 0)
6470
6753
  });
6471
6754
 
6472
6755
  outputs.push({ [changeAddress]: operationOutput });
@@ -6492,7 +6775,20 @@ function requireTagAddressBuilder () {
6492
6775
  qualifierAssetUsed: qualifierName,
6493
6776
  targetAddresses,
6494
6777
  addressCount,
6495
- operationType: isUntag ? 'UNTAG_ADDRESSES' : 'TAG_ADDRESSES'
6778
+ operationType: isUntag ? 'UNTAG_ADDRESSES' : 'TAG_ADDRESSES',
6779
+ localRawBuild: this.buildLocalRawBuild(
6780
+ isUntag ? 'UNTAG_ADDRESSES' : 'TAG_ADDRESSES',
6781
+ inputs,
6782
+ burnInfo,
6783
+ changeAddress,
6784
+ xnaChange > 0.00000001 ? parseFloat(xnaChange.toFixed(8)) : null,
6785
+ {
6786
+ qualifierName,
6787
+ targetAddresses,
6788
+ qualifierChangeAddress: changeAddress,
6789
+ qualifierChangeAmountRaw: this.toSatoshis(qualifierQuantity, 0)
6790
+ }
6791
+ )
6496
6792
  }
6497
6793
  );
6498
6794
  }
@@ -6739,7 +7035,24 @@ function requireFreezeAddressBuilder () {
6739
7035
  ownerTokenUsed: ownerTokenName,
6740
7036
  targetAddresses: targetAddresses.length > 0 ? targetAddresses : null,
6741
7037
  addressCount: targetAddresses.length,
6742
- operationType
7038
+ operationType,
7039
+ localRawBuild: this.buildLocalRawBuild(
7040
+ operationType,
7041
+ inputs,
7042
+ null,
7043
+ xnaChange > 0.00000001 ? changeAddress : null,
7044
+ xnaChange > 0.00000001 ? parseFloat(xnaChange.toFixed(8)) : null,
7045
+ operationType === 'FREEZE_ADDRESSES' || operationType === 'UNFREEZE_ADDRESSES'
7046
+ ? {
7047
+ assetName,
7048
+ targetAddresses,
7049
+ ownerChangeAddress: changeAddress
7050
+ }
7051
+ : {
7052
+ assetName,
7053
+ ownerChangeAddress: changeAddress
7054
+ }
7055
+ )
6743
7056
  }
6744
7057
  );
6745
7058
  }
@@ -6916,7 +7229,7 @@ function requireNeuraiAssets () {
6916
7229
  return {
6917
7230
  ...params,
6918
7231
  network: this.config.network,
6919
- addresses: this.config.addresses,
7232
+ walletAddresses: this.config.addresses,
6920
7233
  changeAddress: params.changeAddress || this.config.changeAddress,
6921
7234
  toAddress: params.toAddress || this.config.toAddress
6922
7235
  };