@neuraiproject/neurai-assets 1.3.1 → 1.3.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
@@ -24,6 +24,31 @@ Complete asset management library for Neurai blockchain. Supports creation, reis
24
24
  | **RESTRICTED** | `$SECURITY` | 3000 XNA | Security token with compliance |
25
25
  | **DEPIN** | `&DEVICE` or `&DEVICE/ROUTER001` | 10 XNA | Soulbound asset with holder validity controls |
26
26
 
27
+ ## Quantities and asset units
28
+
29
+ Every `quantity` / `asset_quantity` parameter accepted by this library is a
30
+ **user-facing display amount** — the same number a human would write to mean
31
+ "this many tokens". For an asset with `units = 2`, `quantity: 10.50` means
32
+ ten and a half tokens; for an asset with `units = 0`, `quantity: 1` means
33
+ one whole token.
34
+
35
+ Internally the daemon parses the JSON `asset_quantity` field with
36
+ `AmountFromValue` ([Bitcoin-style decimal → 10⁸ sats][amount-from-value])
37
+ and validates that the resulting CAmount is a multiple of `10^(8 − units)`
38
+ via `CheckAmountWithUnits`. Because the chain already does the ×10⁸ scaling
39
+ itself, **the library must NOT pre-multiply** the value. Sending the raw
40
+ display number is the only correct behavior; any extra factor on the wire
41
+ either silently inflates the minted supply (`× 10⁸ → 100,000,000` tokens
42
+ where the user asked for 1) or trips the daemon's
43
+ `ParseFixedPoint` cap with `Invalid amount (3): …`.
44
+
45
+ This was regressed in `1.2.2`/`1.3.x` (a hardcoded `× 10⁸` was added to
46
+ `BaseAssetTransactionBuilder.toSatoshis`) and fixed in the version after
47
+ `1.3.1`. If you write a custom builder, follow the same convention: pass
48
+ the user amount through unchanged, let the daemon scale.
49
+
50
+ [amount-from-value]: https://github.com/NeuraiProject/Neurai-DePIN/blob/main/src/rpc/server.cpp
51
+
27
52
  ## Installation
28
53
 
29
54
  ```bash
@@ -105,8 +130,9 @@ const assetsPQ = new NeuraiAssets(rpc, {
105
130
  ```javascript
106
131
  const result = await assets.createRootAsset({
107
132
  assetName: 'MYTOKEN',
108
- quantity: 1000000, // Total supply
109
- units: 2, // Decimals (0-8)
133
+ quantity: 1000000, // Total supply, in display units (1,000,000 tokens)
134
+ units: 2, // Decimal precision (08). With units=2, fractional
135
+ // values down to 0.01 are allowed.
110
136
  reissuable: true, // Allow reissuance
111
137
  hasIpfs: true,
112
138
  ipfsHash: 'QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG'
@@ -131,12 +157,18 @@ const result = await assets.createSubAsset({
131
157
  // Requires the asset's owner token (MYTOKEN!)
132
158
  const result = await assets.reissueAsset({
133
159
  assetName: 'MYTOKEN',
134
- quantity: 500000, // Additional amount to mint
160
+ quantity: 500000, // Additional amount to mint, in display units.
161
+ // For an asset with units=0, `quantity: 1`
162
+ // mints exactly 1 token (NOT 100,000,000).
135
163
  reissuable: true, // false = lock supply permanently
136
- newIpfs: 'Qm...' // Update IPFS (optional)
164
+ newIpfs: 'Qm...' // Update IPFS (optional)
137
165
  });
138
166
  ```
139
167
 
168
+ > **Note**: `units` cannot be passed to `reissueAsset` — the chain inherits
169
+ > the asset's existing precision (use `new_units` in the raw output if you
170
+ > ever need to change it, but this library doesn't expose that today).
171
+
140
172
  ### Create DEPIN Asset
141
173
 
142
174
  ```javascript
@@ -465,7 +497,7 @@ The library automatically validates that the owner token is returned in each ope
465
497
  | Reissue ROOT/SUB | 200 |
466
498
  | Reissue DEPIN | 200 |
467
499
  | Reissue RESTRICTED | 200 |
468
- | Tag/Untag address | 0.1 (per address) |
500
+ | Tag/Untag address | 0 (network fee only; spends 1 unit of the qualifier per address) |
469
501
  | Freeze/Unfreeze address | 0 (network fee only) |
470
502
  | Freeze/Unfreeze global | 0 (network fee only) |
471
503
 
@@ -476,7 +508,7 @@ The library automatically validates that the owner token is returned in each ope
476
508
  The library validates client-side:
477
509
 
478
510
  ✅ Asset names (format, length, allowed characters)
479
- ✅ Amounts (not exceeding max supply of 21 billion)
511
+ ✅ Amounts (not exceeding max supply of 21 billion display tokens)
480
512
  ✅ Decimals (0-8)
481
513
  ✅ IPFS hashes (valid format)
482
514
  ✅ Verifier strings (boolean logic syntax)
@@ -485,6 +517,11 @@ The library validates client-side:
485
517
  ✅ Owner tokens returned (prevents loss)
486
518
  ✅ Address prefixes by network
487
519
 
520
+ The daemon also enforces server-side that quantities respect the asset's
521
+ precision (`CheckAmountWithUnits` — see [Quantities and asset units](#quantities-and-asset-units)),
522
+ so e.g. trying to issue `0.1` of a `units=0` asset is rejected with
523
+ `min-qty-not-multiple-of-units` regardless of what the client sent.
524
+
488
525
  ## Network Configuration
489
526
 
490
527
  ```javascript
@@ -4606,24 +4606,47 @@ var NeuraiAssetsBundle = (function (exports) {
4606
4606
  }
4607
4607
 
4608
4608
  /**
4609
- * Convert asset amount to protocol raw units.
4610
- * Asset raw quantities in transaction payloads are always encoded with
4611
- * 8 decimal places, regardless of the asset's displayed `units`.
4609
+ * Build the JSON `asset_quantity` value for a `createrawtransaction`
4610
+ * output (issue / reissue / tag change_quantity / etc.).
4611
+ *
4612
+ * The chain parses this field with `AmountFromValue()` (Bitcoin-style
4613
+ * decimal-XNA → 10^8 sats), then validates that the resulting CAmount
4614
+ * is a multiple of `10^(8 - units)` via `CheckAmountWithUnits`
4615
+ * (assets.cpp). So:
4616
+ *
4617
+ * - The JSON value MUST be the user-facing display amount
4618
+ * (e.g. "1" for one token, "1.5" for one and a half tokens).
4619
+ * - The lib must NOT pre-multiply by 10^8 or 10^units; the daemon
4620
+ * does the 10^8 scaling itself, and any extra factor here lands
4621
+ * duplicated and inflates the minted supply (or trips the
4622
+ * ParseFixedPoint `exponent >= 18` cap → "Invalid amount (3)").
4623
+ *
4624
+ * History: pre-1.2.2 multiplied by 10^units (correct only for units=0
4625
+ * assets, inflated everything else by 10^units). v1.2.2 changed to
4626
+ * always 10^8 (correct only for units=8, inflated everything else by
4627
+ * 10^8 — e.g. reissuing 1 token of a units=0 asset minted 100,000,000).
4628
+ * The right answer is to send the value untouched.
4629
+ *
4630
+ * The `units` parameter is kept for API compatibility but is unused.
4612
4631
  *
4613
4632
  * @param {number} amount - User-facing asset amount
4614
- * @param {number} units - Asset decimal places (kept for API compatibility)
4615
- * @returns {number} Amount in protocol raw units
4633
+ * @param {number} units - Asset decimal places (unused; kept for API)
4634
+ * @returns {number} The user-facing amount, ready for the JSON output
4616
4635
  */
4617
4636
  toSatoshis(amount, units) {
4618
- return Math.round(amount * 100000000);
4637
+ return amount;
4619
4638
  }
4620
4639
 
4621
4640
  /**
4622
- * Convert protocol raw units back to a user-facing asset amount.
4641
+ * Convert a chain-side asset balance / UTXO satoshis value back to a
4642
+ * user-facing amount. The chain consistently encodes asset balances
4643
+ * in 10^8 sats (because everything goes through AmountFromValue on
4644
+ * the way in), so the divisor is always 10^8 — independent of the
4645
+ * asset's `units`.
4623
4646
  *
4624
- * @param {number} satoshis - Amount in protocol raw units
4625
- * @param {number} units - Asset decimal places (kept for API compatibility)
4626
- * @returns {number} Amount in asset units
4647
+ * @param {number} satoshis - Chain value in 10^8 sats
4648
+ * @param {number} units - Asset decimal places (unused; kept for API)
4649
+ * @returns {number} User-facing asset amount
4627
4650
  */
4628
4651
  fromSatoshis(satoshis, units) {
4629
4652
  return satoshis / 100000000;