@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.cjs CHANGED
@@ -4173,7 +4173,7 @@ function requireBaseAssetTransactionBuilder () {
4173
4173
  addresses === undefined
4174
4174
  ) {
4175
4175
  network = networkOrParams.network;
4176
- actualAddresses = networkOrParams.addresses;
4176
+ actualAddresses = networkOrParams.walletAddresses || networkOrParams.addresses;
4177
4177
  actualParams = networkOrParams;
4178
4178
  } else {
4179
4179
  network = networkOrParams;
@@ -4377,23 +4377,163 @@ function requireBaseAssetTransactionBuilder () {
4377
4377
  }
4378
4378
 
4379
4379
  /**
4380
- * Convert amount to satoshis
4381
- * @param {number} amount - Amount in asset units
4382
- * @param {number} units - Decimal places
4383
- * @returns {number} Amount in satoshis
4380
+ * Convert asset amount to protocol raw units.
4381
+ * Asset raw quantities in transaction payloads are always encoded with
4382
+ * 8 decimal places, regardless of the asset's displayed `units`.
4383
+ *
4384
+ * @param {number} amount - User-facing asset amount
4385
+ * @param {number} units - Asset decimal places (kept for API compatibility)
4386
+ * @returns {number} Amount in protocol raw units
4384
4387
  */
4385
4388
  toSatoshis(amount, units) {
4386
- return Math.round(amount * Math.pow(10, units));
4389
+ return Math.round(amount * 100000000);
4387
4390
  }
4388
4391
 
4389
4392
  /**
4390
- * Convert satoshis to amount
4391
- * @param {number} satoshis - Amount in satoshis
4392
- * @param {number} units - Decimal places
4393
+ * Convert protocol raw units back to a user-facing asset amount.
4394
+ *
4395
+ * @param {number} satoshis - Amount in protocol raw units
4396
+ * @param {number} units - Asset decimal places (kept for API compatibility)
4393
4397
  * @returns {number} Amount in asset units
4394
4398
  */
4395
4399
  fromSatoshis(satoshis, units) {
4396
- return satoshis / Math.pow(10, units);
4400
+ return satoshis / 100000000;
4401
+ }
4402
+
4403
+ /**
4404
+ * Normalize builder inputs to raw transaction inputs
4405
+ * @param {Array} inputs - Builder inputs
4406
+ * @returns {Array<{txid: string, vout: number}>} Raw transaction inputs
4407
+ */
4408
+ toRawTxInputs(inputs) {
4409
+ return (inputs || []).map(input => ({
4410
+ txid: input.txid,
4411
+ vout: input.vout !== undefined ? input.vout : input.outputIndex
4412
+ }));
4413
+ }
4414
+
4415
+ /**
4416
+ * Convert XNA amount to satoshis
4417
+ * @param {number|null|undefined} amount - Amount in XNA
4418
+ * @returns {number|undefined} Amount in satoshis
4419
+ */
4420
+ xnaToSatoshis(amount) {
4421
+ if (amount === undefined || amount === null) {
4422
+ return undefined;
4423
+ }
4424
+
4425
+ return Math.round(amount * 100000000);
4426
+ }
4427
+
4428
+ /**
4429
+ * Build a typed local raw build payload compatible with
4430
+ * @neuraiproject/neurai-create-transaction createFromOperation(...)
4431
+ *
4432
+ * @param {string} operationType - Operation type
4433
+ * @param {Array} inputs - Builder inputs
4434
+ * @param {object|null} burnInfo - Burn metadata
4435
+ * @param {string|null} changeAddress - XNA change address
4436
+ * @param {number|null} changeAmount - XNA change amount in XNA
4437
+ * @param {object} operationParams - Operation-specific params
4438
+ * @returns {{ operationType: string, params: object }} Local raw build
4439
+ */
4440
+ buildLocalRawBuild(
4441
+ operationType,
4442
+ inputs,
4443
+ burnInfo = null,
4444
+ changeAddress = null,
4445
+ changeAmount = null,
4446
+ operationParams = {}
4447
+ ) {
4448
+ const params = {
4449
+ inputs: this.toRawTxInputs(inputs),
4450
+ ...operationParams
4451
+ };
4452
+
4453
+ if (burnInfo && burnInfo.address && burnInfo.amount !== undefined && burnInfo.amount !== null) {
4454
+ params.burnAddress = burnInfo.address;
4455
+ params.burnAmountSats = this.xnaToSatoshis(burnInfo.amount);
4456
+ }
4457
+
4458
+ if (changeAddress && changeAmount !== undefined && changeAmount !== null) {
4459
+ params.xnaChangeAddress = changeAddress;
4460
+ params.xnaChangeSats = this.xnaToSatoshis(changeAmount);
4461
+ }
4462
+
4463
+ return {
4464
+ operationType,
4465
+ params
4466
+ };
4467
+ }
4468
+
4469
+ /**
4470
+ * Normalize ordered outputs into flat entries
4471
+ * @param {Array|object} outputs - Transaction outputs
4472
+ * @returns {Array<{address: string, value: unknown}>} Output entries
4473
+ */
4474
+ getOutputEntries(outputs) {
4475
+ if (Array.isArray(outputs)) {
4476
+ return outputs.map(output => {
4477
+ const [address, value] = Object.entries(output)[0];
4478
+ return { address, value };
4479
+ });
4480
+ }
4481
+
4482
+ if (outputs && typeof outputs === 'object') {
4483
+ return Object.entries(outputs).map(([address, value]) => ({ address, value }));
4484
+ }
4485
+
4486
+ return [];
4487
+ }
4488
+
4489
+ /**
4490
+ * Extract burn metadata from outputs
4491
+ * @param {Array<{address: string, value: unknown}>} entries - Output entries
4492
+ * @param {number} burnAmount - Burn amount in XNA
4493
+ * @returns {{ burnAddress: string|null, burnAmount: number }}
4494
+ */
4495
+ extractBurnMetadata(entries, burnAmount) {
4496
+ if (!burnAmount || burnAmount <= 0) {
4497
+ return {
4498
+ burnAddress: null,
4499
+ burnAmount: 0
4500
+ };
4501
+ }
4502
+
4503
+ const burnEntry = entries.find(({ address, value }) => {
4504
+ return typeof value === 'number' &&
4505
+ value === burnAmount &&
4506
+ this.burnManager.isBurnAddress(address);
4507
+ });
4508
+
4509
+ return {
4510
+ burnAddress: burnEntry ? burnEntry.address : null,
4511
+ burnAmount
4512
+ };
4513
+ }
4514
+
4515
+ /**
4516
+ * Extract XNA change metadata from outputs
4517
+ * @param {Array<{address: string, value: unknown}>} entries - Output entries
4518
+ * @param {string|null} burnAddress - Burn address if present
4519
+ * @returns {{ changeAddress: string|null, changeAmount: number|null }}
4520
+ */
4521
+ extractChangeMetadata(entries, burnAddress = null) {
4522
+ const xnaOutputs = entries.filter(({ address, value }) => {
4523
+ return typeof value === 'number' && address !== burnAddress;
4524
+ });
4525
+
4526
+ if (xnaOutputs.length !== 1) {
4527
+ return {
4528
+ changeAddress: null,
4529
+ changeAmount: null
4530
+ };
4531
+ }
4532
+
4533
+ return {
4534
+ changeAddress: xnaOutputs[0].address,
4535
+ changeAmount: xnaOutputs[0].value
4536
+ };
4397
4537
  }
4398
4538
 
4399
4539
  /**
@@ -4408,13 +4548,26 @@ function requireBaseAssetTransactionBuilder () {
4408
4548
  * @returns {object} Formatted result
4409
4549
  */
4410
4550
  formatResult(rawTx, utxos, inputs, outputs, fee, burnAmount, extra = {}) {
4551
+ const outputEntries = this.getOutputEntries(outputs);
4552
+ const burnMetadata = this.extractBurnMetadata(outputEntries, burnAmount);
4553
+ const changeMetadata = this.extractChangeMetadata(outputEntries, burnMetadata.burnAddress);
4554
+
4411
4555
  return {
4412
4556
  rawTx,
4413
4557
  utxos,
4414
4558
  inputs,
4415
4559
  outputs,
4416
4560
  fee,
4417
- burnAmount,
4561
+ burnAmount: burnMetadata.burnAmount,
4562
+ network: this.network,
4563
+ buildStrategy: 'rpc-node',
4564
+ burnAddress: burnMetadata.burnAddress,
4565
+ changeAddress: extra.changeAddress !== undefined
4566
+ ? extra.changeAddress
4567
+ : changeMetadata.changeAddress,
4568
+ changeAmount: extra.changeAmount !== undefined
4569
+ ? extra.changeAmount
4570
+ : changeMetadata.changeAmount,
4418
4571
  ...extra
4419
4572
  };
4420
4573
  }
@@ -4631,7 +4784,23 @@ function requireIssueRootBuilder () {
4631
4784
  {
4632
4785
  assetName,
4633
4786
  ownerTokenName: assetName + '!',
4634
- operationType: 'ISSUE_ROOT'
4787
+ operationType: 'ISSUE_ROOT',
4788
+ localRawBuild: this.buildLocalRawBuild(
4789
+ 'ISSUE_ROOT',
4790
+ inputs,
4791
+ burnInfo,
4792
+ changeAddress,
4793
+ xnaChange > 0.00000001 ? parseFloat(xnaChange.toFixed(8)) : null,
4794
+ {
4795
+ toAddress,
4796
+ assetName,
4797
+ quantityRaw: this.toSatoshis(quantity, units),
4798
+ units,
4799
+ reissuable,
4800
+ ipfsHash: hasIpfs ? ipfsHash : undefined,
4801
+ ownerTokenAddress: changeAddress
4802
+ }
4803
+ )
4635
4804
  }
4636
4805
  );
4637
4806
  }
@@ -4877,7 +5046,23 @@ function requireIssueSubBuilder () {
4877
5046
  parentAssetName,
4878
5047
  ownerTokenName: assetName + '!',
4879
5048
  parentOwnerTokenUsed: ownerTokenName,
4880
- operationType: 'ISSUE_SUB'
5049
+ operationType: 'ISSUE_SUB',
5050
+ localRawBuild: this.buildLocalRawBuild(
5051
+ 'ISSUE_SUB',
5052
+ inputs,
5053
+ burnInfo,
5054
+ changeAddress,
5055
+ xnaChange > 0.00000001 ? parseFloat(xnaChange.toFixed(8)) : null,
5056
+ {
5057
+ toAddress,
5058
+ assetName,
5059
+ quantityRaw: this.toSatoshis(quantity, units),
5060
+ units,
5061
+ reissuable,
5062
+ ipfsHash: hasIpfs ? ipfsHash : undefined,
5063
+ parentOwnerAddress: changeAddress
5064
+ }
5065
+ )
4881
5066
  }
4882
5067
  );
4883
5068
  }
@@ -5030,7 +5215,22 @@ function requireIssueDepinBuilder () {
5030
5215
  {
5031
5216
  assetName,
5032
5217
  ownerTokenName: `${assetName}!`,
5033
- operationType: 'ISSUE_DEPIN'
5218
+ operationType: 'ISSUE_DEPIN',
5219
+ localRawBuild: this.buildLocalRawBuild(
5220
+ 'ISSUE_DEPIN',
5221
+ inputs,
5222
+ burnInfo,
5223
+ changeAddress,
5224
+ xnaChange > 0.00000001 ? parseFloat(xnaChange.toFixed(8)) : null,
5225
+ {
5226
+ toAddress,
5227
+ assetName,
5228
+ quantityRaw: this.toSatoshis(quantity, 0),
5229
+ ipfsHash: hasIpfs ? ipfsHash : undefined,
5230
+ ownerTokenAddress: changeAddress,
5231
+ reissuable
5232
+ }
5233
+ )
5034
5234
  }
5035
5235
  );
5036
5236
  }
@@ -5278,7 +5478,23 @@ function requireReissueBuilder () {
5278
5478
  newTotalSupply,
5279
5479
  previousSupply: currentSupply,
5280
5480
  reissuableLocked: reissuable === false,
5281
- operationType: 'REISSUE'
5481
+ operationType: 'REISSUE',
5482
+ localRawBuild: this.buildLocalRawBuild(
5483
+ 'REISSUE',
5484
+ inputs,
5485
+ burnInfo,
5486
+ changeAddress,
5487
+ xnaChange > 0.00000001 ? parseFloat(xnaChange.toFixed(8)) : null,
5488
+ {
5489
+ toAddress,
5490
+ assetName,
5491
+ quantityRaw: this.toSatoshis(quantity, units),
5492
+ units,
5493
+ reissuable: reissuable !== undefined ? reissuable : undefined,
5494
+ ipfsHash: newIpfs || undefined,
5495
+ ownerChangeAddress: isDepinAsset ? toAddress : changeAddress
5496
+ }
5497
+ )
5282
5498
  }
5283
5499
  );
5284
5500
  }
@@ -5535,7 +5751,21 @@ function requireIssueUniqueBuilder () {
5535
5751
  createdNFTs,
5536
5752
  nftCount,
5537
5753
  ownerTokenUsed: ownerTokenName,
5538
- operationType: 'ISSUE_UNIQUE'
5754
+ operationType: 'ISSUE_UNIQUE',
5755
+ localRawBuild: this.buildLocalRawBuild(
5756
+ 'ISSUE_UNIQUE',
5757
+ inputs,
5758
+ burnInfo,
5759
+ changeAddress,
5760
+ xnaChange > 0.00000001 ? parseFloat(xnaChange.toFixed(8)) : null,
5761
+ {
5762
+ toAddress,
5763
+ rootName,
5764
+ assetTags,
5765
+ ipfsHashes: ipfsHashes.length > 0 ? ipfsHashes : undefined,
5766
+ ownerTokenAddress: changeAddress
5767
+ }
5768
+ )
5539
5769
  }
5540
5770
  );
5541
5771
  }
@@ -5750,11 +5980,13 @@ function requireIssueQualifierBuilder () {
5750
5980
  // Last: Issue qualifier operation
5751
5981
  const issueQualifierOutput = OutputFormatter.formatIssueQualifierOutput({
5752
5982
  asset_name: assetName,
5753
- asset_quantity: quantity,
5983
+ asset_quantity: this.toSatoshis(quantity, 0),
5754
5984
  has_ipfs: hasIpfs,
5755
5985
  ipfs_hash: ipfsHash,
5756
5986
  root_change_address: isSub ? changeAddress : undefined,
5757
- change_quantity: isSub ? parentQualifierQuantity : undefined
5987
+ change_quantity: isSub && parentQualifierQuantity !== null
5988
+ ? this.toSatoshis(parentQualifierQuantity, 0)
5989
+ : undefined
5758
5990
  });
5759
5991
 
5760
5992
  outputs.push({ [toAddress]: issueQualifierOutput });
@@ -5780,7 +6012,24 @@ function requireIssueQualifierBuilder () {
5780
6012
  qualifierType: isSub ? 'SUB_QUALIFIER' : 'QUALIFIER',
5781
6013
  parentQualifier: isSub ? parsed.parent : null,
5782
6014
  parentQualifierUsed: parentQualifierName,
5783
- operationType: isSub ? 'ISSUE_SUB_QUALIFIER' : 'ISSUE_QUALIFIER'
6015
+ operationType: isSub ? 'ISSUE_SUB_QUALIFIER' : 'ISSUE_QUALIFIER',
6016
+ localRawBuild: this.buildLocalRawBuild(
6017
+ isSub ? 'ISSUE_SUB_QUALIFIER' : 'ISSUE_QUALIFIER',
6018
+ inputs,
6019
+ burnInfo,
6020
+ changeAddress,
6021
+ xnaChange > 0.00000001 ? parseFloat(xnaChange.toFixed(8)) : null,
6022
+ {
6023
+ toAddress,
6024
+ assetName,
6025
+ quantityRaw: this.toSatoshis(quantity, 0),
6026
+ ipfsHash: hasIpfs ? ipfsHash : undefined,
6027
+ rootChangeAddress: isSub ? changeAddress : undefined,
6028
+ changeQuantityRaw: isSub && parentQualifierQuantity !== null
6029
+ ? this.toSatoshis(parentQualifierQuantity, 0)
6030
+ : undefined
6031
+ }
6032
+ )
5784
6033
  }
5785
6034
  );
5786
6035
  }
@@ -6010,7 +6259,24 @@ function requireIssueRestrictedBuilder () {
6010
6259
  ownerTokenName,
6011
6260
  verifierString,
6012
6261
  requiredQualifiers,
6013
- operationType: 'ISSUE_RESTRICTED'
6262
+ operationType: 'ISSUE_RESTRICTED',
6263
+ localRawBuild: this.buildLocalRawBuild(
6264
+ 'ISSUE_RESTRICTED',
6265
+ inputs,
6266
+ burnInfo,
6267
+ changeAddress,
6268
+ xnaChange > 0.00000001 ? parseFloat(xnaChange.toFixed(8)) : null,
6269
+ {
6270
+ toAddress,
6271
+ assetName,
6272
+ quantityRaw: this.toSatoshis(quantity, units),
6273
+ verifierString,
6274
+ units,
6275
+ reissuable,
6276
+ ipfsHash: hasIpfs ? ipfsHash : undefined,
6277
+ ownerChangeAddress: changeAddress
6278
+ }
6279
+ )
6014
6280
  }
6015
6281
  );
6016
6282
  }
@@ -6273,7 +6539,24 @@ function requireReissueRestrictedBuilder () {
6273
6539
  newVerifier: changeVerifier ? newVerifier : undefined,
6274
6540
  requiredQualifiers,
6275
6541
  reissuableLocked: reissuable === false,
6276
- operationType: 'REISSUE_RESTRICTED'
6542
+ operationType: 'REISSUE_RESTRICTED',
6543
+ localRawBuild: this.buildLocalRawBuild(
6544
+ 'REISSUE_RESTRICTED',
6545
+ inputs,
6546
+ burnInfo,
6547
+ changeAddress,
6548
+ xnaChange > 0.00000001 ? parseFloat(xnaChange.toFixed(8)) : null,
6549
+ {
6550
+ toAddress,
6551
+ assetName,
6552
+ quantityRaw: this.toSatoshis(quantity, assetData.units || 0),
6553
+ units: assetData.units || 0,
6554
+ reissuable: reissuable !== undefined ? reissuable : undefined,
6555
+ ipfsHash: newIpfs || undefined,
6556
+ ownerChangeAddress: this.params.ownerChangeAddress || changeAddress,
6557
+ verifierString: changeVerifier ? newVerifier : undefined
6558
+ }
6559
+ )
6277
6560
  }
6278
6561
  );
6279
6562
  }
@@ -6465,12 +6748,12 @@ function requireTagAddressBuilder () {
6465
6748
  ? OutputFormatter.formatUntagAddressesOutput({
6466
6749
  qualifier: qualifierName,
6467
6750
  addresses: targetAddresses,
6468
- change_quantity: qualifierQuantity
6751
+ change_quantity: this.toSatoshis(qualifierQuantity, 0)
6469
6752
  })
6470
6753
  : OutputFormatter.formatTagAddressesOutput({
6471
6754
  qualifier: qualifierName,
6472
6755
  addresses: targetAddresses,
6473
- change_quantity: qualifierQuantity
6756
+ change_quantity: this.toSatoshis(qualifierQuantity, 0)
6474
6757
  });
6475
6758
 
6476
6759
  outputs.push({ [changeAddress]: operationOutput });
@@ -6496,7 +6779,20 @@ function requireTagAddressBuilder () {
6496
6779
  qualifierAssetUsed: qualifierName,
6497
6780
  targetAddresses,
6498
6781
  addressCount,
6499
- operationType: isUntag ? 'UNTAG_ADDRESSES' : 'TAG_ADDRESSES'
6782
+ operationType: isUntag ? 'UNTAG_ADDRESSES' : 'TAG_ADDRESSES',
6783
+ localRawBuild: this.buildLocalRawBuild(
6784
+ isUntag ? 'UNTAG_ADDRESSES' : 'TAG_ADDRESSES',
6785
+ inputs,
6786
+ burnInfo,
6787
+ changeAddress,
6788
+ xnaChange > 0.00000001 ? parseFloat(xnaChange.toFixed(8)) : null,
6789
+ {
6790
+ qualifierName,
6791
+ targetAddresses,
6792
+ qualifierChangeAddress: changeAddress,
6793
+ qualifierChangeAmountRaw: this.toSatoshis(qualifierQuantity, 0)
6794
+ }
6795
+ )
6500
6796
  }
6501
6797
  );
6502
6798
  }
@@ -6743,7 +7039,24 @@ function requireFreezeAddressBuilder () {
6743
7039
  ownerTokenUsed: ownerTokenName,
6744
7040
  targetAddresses: targetAddresses.length > 0 ? targetAddresses : null,
6745
7041
  addressCount: targetAddresses.length,
6746
- operationType
7042
+ operationType,
7043
+ localRawBuild: this.buildLocalRawBuild(
7044
+ operationType,
7045
+ inputs,
7046
+ null,
7047
+ xnaChange > 0.00000001 ? changeAddress : null,
7048
+ xnaChange > 0.00000001 ? parseFloat(xnaChange.toFixed(8)) : null,
7049
+ operationType === 'FREEZE_ADDRESSES' || operationType === 'UNFREEZE_ADDRESSES'
7050
+ ? {
7051
+ assetName,
7052
+ targetAddresses,
7053
+ ownerChangeAddress: changeAddress
7054
+ }
7055
+ : {
7056
+ assetName,
7057
+ ownerChangeAddress: changeAddress
7058
+ }
7059
+ )
6747
7060
  }
6748
7061
  );
6749
7062
  }
@@ -6920,7 +7233,7 @@ function requireNeuraiAssets () {
6920
7233
  return {
6921
7234
  ...params,
6922
7235
  network: this.config.network,
6923
- addresses: this.config.addresses,
7236
+ walletAddresses: this.config.addresses,
6924
7237
  changeAddress: params.changeAddress || this.config.changeAddress,
6925
7238
  toAddress: params.toAddress || this.config.toAddress
6926
7239
  };