@neuraiproject/neurai-assets 1.5.1 → 1.5.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.
@@ -6309,11 +6309,14 @@ var NeuraiAssetsBundle = (function (exports) {
6309
6309
  * @throws {InsufficientFundsError} If not enough funds
6310
6310
  */
6311
6311
  async selectBaseCurrencyUTXOs(addresses, requiredAmount, buffer = 0.1, options = {}) {
6312
- // Get all XNA UTXOs
6313
- const allUTXOs = await this.getUTXOs(addresses, null);
6314
-
6315
- // Get mempool and filter
6316
- const mempool = await this.getMempoolEntries(addresses);
6312
+ // Both reads describe the same addresses and neither feeds the other: the
6313
+ // mempool result only filters the UTXO result afterwards. Awaiting them in
6314
+ // sequence spent one extra network round trip per selection, which on a
6315
+ // remote RPC proxy is most of the time a wallet spends building anything.
6316
+ const [allUTXOs, mempool] = await Promise.all([
6317
+ this.getUTXOs(addresses, null),
6318
+ this.getMempoolEntries(addresses)
6319
+ ]);
6317
6320
  const unspentUTXOs = this.filterMempoolSpentUTXOs(allUTXOs, mempool);
6318
6321
 
6319
6322
  // Drop outpoints the caller already spends elsewhere in this transaction
@@ -6379,11 +6382,14 @@ var NeuraiAssetsBundle = (function (exports) {
6379
6382
  throw new Error('Asset name is required');
6380
6383
  }
6381
6384
 
6382
- // Get all asset UTXOs
6383
- const allUTXOs = await this.getUTXOs(addresses, assetName);
6384
-
6385
- // Get mempool and filter
6386
- const mempool = await this.getMempoolEntries(addresses);
6385
+ // Both reads describe the same addresses and neither feeds the other: the
6386
+ // mempool result only filters the UTXO result afterwards. Awaiting them in
6387
+ // sequence spent one extra network round trip per selection, which on a
6388
+ // remote RPC proxy is most of the time a wallet spends building anything.
6389
+ const [allUTXOs, mempool] = await Promise.all([
6390
+ this.getUTXOs(addresses, assetName),
6391
+ this.getMempoolEntries(addresses)
6392
+ ]);
6387
6393
  const unspentUTXOs = this.filterMempoolSpentUTXOs(allUTXOs, mempool);
6388
6394
 
6389
6395
  const excluded = toOutpointSet(options.exclude);
@@ -6505,8 +6511,10 @@ var NeuraiAssetsBundle = (function (exports) {
6505
6511
  * @returns {Promise<bigint>} Total balance in 10^8-scaled units
6506
6512
  */
6507
6513
  async getBalanceRaw(addresses, assetName = null) {
6508
- const utxos = await this.getUTXOs(addresses, assetName);
6509
- const mempool = await this.getMempoolEntries(addresses);
6514
+ const [utxos, mempool] = await Promise.all([
6515
+ this.getUTXOs(addresses, assetName),
6516
+ this.getMempoolEntries(addresses)
6517
+ ]);
6510
6518
  const availableUTXOs = this.filterMempoolSpentUTXOs(utxos, mempool);
6511
6519
 
6512
6520
  return sumProtocolIntegers(availableUTXOs, 'satoshis', `${assetName || 'XNA'} utxo.satoshis`);
@@ -7994,6 +8002,44 @@ var NeuraiAssetsBundle = (function (exports) {
7994
8002
  throw new Error('build must be implemented by subclass');
7995
8003
  }
7996
8004
 
8005
+ /**
8006
+ * Start the fee-rate lookup without waiting for it.
8007
+ *
8008
+ * Every build needs the fee rate, and it depends on nothing the build
8009
+ * computes, so there is no reason for it to wait its turn behind the reads
8010
+ * that come first. Kicking it off early lets it share a round trip with
8011
+ * them; `estimateFee`/`estimateFeeSats` await the same memoised promise, so
8012
+ * the call still happens exactly once and a failure still surfaces there.
8013
+ *
8014
+ * The trailing `catch` only marks the promise as handled while nothing is
8015
+ * awaiting it — it attaches to a derived promise, so the original still
8016
+ * rejects for whoever awaits it later.
8017
+ *
8018
+ * @returns {void}
8019
+ */
8020
+ warmFeeRate() {
8021
+ if (this._feeRatePromise) {
8022
+ return;
8023
+ }
8024
+ this._feeRatePromise = this.utxoSelector.getFeeRate();
8025
+ this._feeRatePromise.catch(() => {});
8026
+ }
8027
+
8028
+ /**
8029
+ * Start every read a build needs but that depends on nothing the build
8030
+ * computes: the fee rate and the NIP-040 asset marker.
8031
+ *
8032
+ * Both are memoised, so warming them costs no extra call — it only moves
8033
+ * them off the critical path. Every builder that reaches here goes on to
8034
+ * stamp a marker, so neither read is ever speculative.
8035
+ *
8036
+ * @returns {void}
8037
+ */
8038
+ warmChainReads() {
8039
+ this.warmFeeRate();
8040
+ this.resolveAssetMarker().catch(() => {});
8041
+ }
8042
+
7997
8043
  /**
7998
8044
  * Estimate transaction fee.
7999
8045
  *
@@ -8007,9 +8053,7 @@ var NeuraiAssetsBundle = (function (exports) {
8007
8053
  * @returns {Promise<number>} Estimated fee in XNA
8008
8054
  */
8009
8055
  async estimateFee(inputs, outputs) {
8010
- if (!this._feeRatePromise) {
8011
- this._feeRatePromise = this.utxoSelector.getFeeRate();
8012
- }
8056
+ this.warmFeeRate();
8013
8057
  const feeRate = await this._feeRatePromise;
8014
8058
  return this.utxoSelector.estimateFee(inputs, outputs, feeRate);
8015
8059
  }
@@ -8040,9 +8084,7 @@ var NeuraiAssetsBundle = (function (exports) {
8040
8084
  * @returns {Promise<bigint>} Estimated fee in satoshis
8041
8085
  */
8042
8086
  async estimateFeeSats(inputs, outputs) {
8043
- if (!this._feeRatePromise) {
8044
- this._feeRatePromise = this.utxoSelector.getFeeRate();
8045
- }
8087
+ this.warmFeeRate();
8046
8088
  const feeRate = await this._feeRatePromise;
8047
8089
  return this.utxoSelector.estimateFeeSats(inputs, outputs, feeRate);
8048
8090
  }
@@ -8085,6 +8127,10 @@ var NeuraiAssetsBundle = (function (exports) {
8085
8127
  initialInputHint = 1
8086
8128
  } = options;
8087
8129
 
8130
+ // Covers the builders that never call assetExists, whose first read is
8131
+ // this one.
8132
+ this.warmChainReads();
8133
+
8088
8134
  const addresses = await this._getAddresses();
8089
8135
  const excluded = UTXOSelector.toOutpointSet(exclude);
8090
8136
  // toOutpointSet returns the caller's Set untouched when it already is one;
@@ -8714,6 +8760,9 @@ var NeuraiAssetsBundle = (function (exports) {
8714
8760
  * @returns {Promise<boolean>} True if exists
8715
8761
  */
8716
8762
  async assetExists(assetName) {
8763
+ // This is the first read a build performs and its answer gates nothing but
8764
+ // the guard below, so let the build's other chain reads travel alongside it.
8765
+ this.warmChainReads();
8717
8766
  try {
8718
8767
  const assetData = await this.rpc('getassetdata', [assetName]);
8719
8768
  return assetData !== null && assetData !== undefined;
@@ -8733,6 +8782,9 @@ var NeuraiAssetsBundle = (function (exports) {
8733
8782
  * @returns {Promise<object|null>} Asset data or null if not found
8734
8783
  */
8735
8784
  async getAssetData(assetName) {
8785
+ // This is the first read a build performs and its answer gates nothing but
8786
+ // the guard below, so let the build's other chain reads travel alongside it.
8787
+ this.warmChainReads();
8736
8788
  try {
8737
8789
  return await this.rpc('getassetdata', [assetName]);
8738
8790
  } catch (error) {