@neuraiproject/neurai-assets 1.6.1 → 1.6.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/README.md CHANGED
@@ -1100,3 +1100,34 @@ Canonical `createTransactionBuild` quantities and changes remain bigint.
1100
1100
  XNA string outputs are preserved during output ordering. Exact selection totals
1101
1101
  remain available as `totalSats` / raw methods; never use a rounded display value
1102
1102
  as a new transaction input.
1103
+
1104
+
1105
+ ## Exact amounts and legacy converter migration
1106
+
1107
+ In 1.6.2, insufficient-funds errors preserve `required` and `available` as
1108
+ `number | string`. Large fractional amounts are returned as exact decimal text;
1109
+ consumers must not convert them to `Number` before computing or displaying funds.
1110
+ `BuildInput.satoshis` accepts exact raw integers and `changeAmount` may be text.
1111
+
1112
+ ```js
1113
+ import { utils } from '@neuraiproject/neurai-assets';
1114
+ const { assetAmountToRaw, rawToDisplayAmount, formatRawAsDecimal } = utils.AssetAmount;
1115
+ const raw = assetAmountToRaw('100000000.00000001', 8);
1116
+ const compatibleDisplay = rawToDisplayAmount(raw); // '100000000.00000001'
1117
+ const decimalText = formatRawAsDecimal(raw); // Always plain decimal text
1118
+ ```
1119
+
1120
+ `utils.AmountConverter` is deprecated but retains its existing behavior in 1.x.
1121
+ It may round even when its result is a safe integer. Its scale is `10^units`;
1122
+ protocol XNA and asset amounts always use `10^8`, with `units` only restricting
1123
+ asset divisibility. For example, legacy `toSatoshis(1.23, 2)` returns `123`, while
1124
+ `assetAmountToRaw('1.23', 2)` returns `123000000n`. Check the unit of stored values
1125
+ before migrating: for a valid legacy integer with units 2, multiplying that exact
1126
+ integer by `1000000n` converts it to protocol raw units. Already rounded digits
1127
+ cannot be recovered. Prefer the original decimal text whenever available.
1128
+
1129
+ Replace protocol decimal-to-raw conversion with `assetAmountToRaw`, and raw-to-display
1130
+ conversion with `rawToDisplayAmount` or `formatRawAsDecimal`. These are not drop-in
1131
+ replacements for legacy formatting or deliberate rounding. Keep user input as
1132
+ text; validate divisibility instead of silently rounding it. A display number can
1133
+ use exponent notation for tiny values, so use `formatRawAsDecimal` for plain text.
@@ -129,7 +129,13 @@ var NeuraiAssetsBundle = (function (exports) {
129
129
  }
130
130
  }
131
131
 
132
+ /** Required and available are exact display amounts, before any funding buffer. */
132
133
  class InsufficientFundsError extends ValidationError {
134
+ /**
135
+ * @param {string} message
136
+ * @param {number|string} required
137
+ * @param {number|string} available
138
+ */
133
139
  constructor(message, required, available) {
134
140
  super(message);
135
141
  this.name = 'InsufficientFundsError';
@@ -4670,26 +4676,9 @@ var NeuraiAssetsBundle = (function (exports) {
4670
4676
  * **through text**: scaling the decimal string keeps every digit the caller
4671
4677
  * wrote, and refuses the ones it cannot keep.
4672
4678
  *
4673
- * The alternative `BigInt(Math.round(value * 1e8))`, which is what
4674
- * `assetUnitsToRaw` in neurai-create-transaction does is correct for
4675
- * ordinary magnitudes. `4.35 * 1e8` is `434999999.99999994`, but `Math.round`
4676
- * recovers `435000000`; that example shows binary representation, not a wrong
4677
- * result. For a finite, non-negative number it has two silent failure modes:
4678
- *
4679
- * - more than eight decimals are rounded away instead of rejected, so an
4680
- * amount can vanish (`1e-9` becomes `0n`) or shift (`1.123456789` becomes
4681
- * `112345679`);
4682
- * - past `Number.MAX_SAFE_INTEGER` a double can no longer represent every
4683
- * integer, so the product may or may not survive — and nothing says which.
4684
- * `184467440.73709551` comes back as `18446744073709552n`, one unit off,
4685
- * while `21000000000` scales to `2100000000000000000n` exactly. The risk
4686
- * is that the two cases are indistinguishable from the outside.
4687
- *
4688
- * Outside that range its `Number(amount || 0)` also turns `NaN`, `null` and
4689
- * `''` into `0n`, accepts negatives, and coerces other types — `true` yields a
4690
- * whole unit. Every one of these is reachable with values a wallet can hold,
4691
- * and none announces itself. Hence: convert by text, validate, and fail closed
4692
- * rather than delegate.
4679
+ * neurai-create-transaction also converts through exact decimal parsing.
4680
+ * This adapter retains the assets API policies: plain decimal strings,
4681
+ * divisibility checks, monetary limits and asset-specific validation errors.
4693
4682
  */
4694
4683
 
4695
4684
  var assetAmount;
@@ -4991,18 +4980,10 @@ var NeuraiAssetsBundle = (function (exports) {
4991
4980
  }
4992
4981
 
4993
4982
  /**
4994
- * Render a protocol integer as a JS number for the legacy display envelopes.
4995
- *
4996
- * Fails closed rather than returning a value the caller cannot trust: a
4997
- * quantity whose display form is not exactly representable would otherwise
4998
- * travel on as a plausible-looking wrong number.
4999
- *
5000
- * @param {bigint} raw - Protocol integer (10^8-scaled)
5001
- * @param {string} [label] - Prefix for error messages
5002
- * @returns {number} Display amount
5003
- * @throws {InvalidAmountError} If the display value is not exactly representable
4983
+ * Render a protocol integer as a compatible display amount.
4984
+ * @param {bigint|string|number} raw - Exact protocol integer
4985
+ * @returns {number|string} Number only when a decimal round-trip retains every unit
5004
4986
  */
5005
- /** Compatibility display: numbers where monetary precision is safe, text otherwise. */
5006
4987
  function rawToDisplayAmount(raw) {
5007
4988
  const value = toProtocolInteger(raw);
5008
4989
  const abs = value < 0n ? -value : value;
@@ -5015,6 +4996,11 @@ var NeuraiAssetsBundle = (function (exports) {
5015
4996
  return formatRawAsDecimal(value);
5016
4997
  }
5017
4998
 
4999
+ /**
5000
+ * @param {bigint} raw - Protocol integer
5001
+ * @param {string} [label] - Error context
5002
+ * @returns {number} Display number, or throws if the decimal round-trip loses units
5003
+ */
5018
5004
  function rawToDisplayNumber(raw, label = 'amount') {
5019
5005
  const text = formatRawAsDecimal(raw);
5020
5006
  const asNumber = Number(text);
@@ -5116,6 +5102,9 @@ var NeuraiAssetsBundle = (function (exports) {
5116
5102
  hasRequiredAmountConverter = 1;
5117
5103
  const { assetAmountToRaw } = requireAssetAmount();
5118
5104
  /**
5105
+ * @deprecated Use utils.AssetAmount for exact protocol amounts.
5106
+ * Legacy scaling is 10^units, not the protocol scale of 10^8.
5107
+ * These methods may round; retained only for backward compatibility.
5119
5108
  * Amount Converter
5120
5109
  * Converts between user amounts and satoshis (protocol internal format)
5121
5110
  */
@@ -6564,8 +6553,8 @@ var NeuraiAssetsBundle = (function (exports) {
6564
6553
  throw new InsufficientFundsError(
6565
6554
  `Insufficient XNA balance. Required: ${required} XNA (+ ${(buffer * 100).toFixed(0)}% buffer), ` +
6566
6555
  `Available: ${available} XNA`,
6567
- Number(required),
6568
- Number(available)
6556
+ rawToDisplayAmount(requiredSats),
6557
+ rawToDisplayAmount(totalSatoshis)
6569
6558
  );
6570
6559
  }
6571
6560
 
@@ -6633,8 +6622,8 @@ var NeuraiAssetsBundle = (function (exports) {
6633
6622
  const required = formatRawAsDecimal(requiredRaw);
6634
6623
  throw new InsufficientFundsError(
6635
6624
  `Insufficient ${assetName} balance. Required: ${required}, Available: ${available}`,
6636
- Number(required),
6637
- Number(available)
6625
+ rawToDisplayAmount(requiredRaw),
6626
+ rawToDisplayAmount(totalSatoshis)
6638
6627
  );
6639
6628
  }
6640
6629
 
@@ -8906,7 +8895,7 @@ var NeuraiAssetsBundle = (function (exports) {
8906
8895
  * Extract XNA change metadata from outputs
8907
8896
  * @param {Array<{address: string, value: unknown}>} entries - Output entries
8908
8897
  * @param {string|null} burnAddress - Burn address if present
8909
- * @returns {{ changeAddress: string|null, changeAmount: number|null }}
8898
+ * @returns {{ changeAddress: string|null, changeAmount: number|string|null }}
8910
8899
  */
8911
8900
  extractChangeMetadata(entries, burnAddress = null) {
8912
8901
  const xnaOutputs = entries.filter(({ address, value }) => {