@neuraiproject/neurai-assets 1.4.0 → 1.5.0
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 +175 -11
- package/dist/NeuraiAssets.global.js +4341 -471
- package/dist/NeuraiAssets.global.js.map +1 -1
- package/dist/browser.js +4341 -471
- package/dist/browser.js.map +1 -1
- package/dist/index.cjs +4341 -471
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +4341 -471
- package/dist/index.js.map +1 -1
- package/index.d.ts +222 -3
- package/package.json +9 -5
package/README.md
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
Complete asset management library for Neurai blockchain. Supports creation, reissuance, and queries for all asset types in a non-custodial way.
|
|
4
4
|
|
|
5
|
+
> **1.4.1**: fix — the constructor dropped `config.assetMarker`, so the
|
|
6
|
+
> wallet-level override documented in 1.4.0 never reached the builders
|
|
7
|
+
> (per-operation `params.assetMarker` was unaffected). Precedence is now
|
|
8
|
+
> effective: `params.assetMarker` > `config.assetMarker` > node.
|
|
9
|
+
>
|
|
5
10
|
> **1.4.0**: NIP-040 `assetMarker` in `localRawBuild` (see below); RPC
|
|
6
11
|
> rejection messages from `@neuraiproject/neurai-rpc` >= 0.5 are surfaced
|
|
7
12
|
> correctly (they carry no `.message`); name-length caps now mirror the node
|
|
@@ -479,18 +484,142 @@ All creation/reissuance operations return an object with this structure:
|
|
|
479
484
|
|
|
480
485
|
```javascript
|
|
481
486
|
{
|
|
482
|
-
rawTx: 'hex string', // Unsigned transaction (to sign with wallet)
|
|
487
|
+
rawTx: 'hex string', // Unsigned transaction built by the node (to sign with wallet)
|
|
483
488
|
utxos: [...], // UTXOs selected for the operation
|
|
484
489
|
inputs: [...], // Transaction inputs
|
|
485
|
-
outputs: [...], // Ordered outputs
|
|
490
|
+
outputs: [...], // Ordered outputs (DISPLAY amounts, for createrawtransaction)
|
|
486
491
|
fee: 0.001, // Fee in XNA
|
|
487
492
|
burnAmount: 1000, // Burned amount in XNA
|
|
488
493
|
assetName: 'MYTOKEN', // Operation-specific fields vary by builder
|
|
489
494
|
ownerTokenName: 'MYTOKEN!',
|
|
490
|
-
operationType: 'ISSUE_ROOT'
|
|
495
|
+
operationType: 'ISSUE_ROOT',
|
|
496
|
+
createTransactionBuild: { ... } // Build it yourself, offline — see below
|
|
491
497
|
}
|
|
492
498
|
```
|
|
493
499
|
|
|
500
|
+
## Building offline with `createTransactionBuild` (1.5.0+)
|
|
501
|
+
|
|
502
|
+
`rawTx` above is built by the node through `createrawtransaction`. To build the
|
|
503
|
+
same transaction **locally**, pass `result.createTransactionBuild` straight to
|
|
504
|
+
`@neuraiproject/neurai-create-transaction`:
|
|
505
|
+
|
|
506
|
+
```javascript
|
|
507
|
+
import { createFromOperation } from '@neuraiproject/neurai-create-transaction';
|
|
508
|
+
|
|
509
|
+
const result = await assets.transferAsset({
|
|
510
|
+
assetName: 'MYTOKEN',
|
|
511
|
+
recipients: [{ address: 'tRecipient...', amount: 4.35 }]
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
const built = createFromOperation(result.createTransactionBuild);
|
|
515
|
+
// built.rawTx — sign it with @neuraiproject/neurai-sign-transaction
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
Nothing has to be renamed, rescaled or reinterpreted in between. In particular
|
|
519
|
+
you do **not** need to:
|
|
520
|
+
|
|
521
|
+
- map the operation type — a transfer already arrives as `STANDARD_TRANSFER` or
|
|
522
|
+
`TRANSFER_DEPIN`, the discriminants the serializer accepts;
|
|
523
|
+
- merge recipients, asset change and the DePIN owner return into one list (the
|
|
524
|
+
serializer emits the `&NAME!` escort itself; listing it again would produce
|
|
525
|
+
two owner outputs);
|
|
526
|
+
- convert display amounts to protocol integers;
|
|
527
|
+
- add or correct `assetMarker`.
|
|
528
|
+
|
|
529
|
+
### Display amounts vs raw amounts
|
|
530
|
+
|
|
531
|
+
The library speaks two representations, deliberately kept apart:
|
|
532
|
+
|
|
533
|
+
| Where | Representation | Why |
|
|
534
|
+
| --- | --- | --- |
|
|
535
|
+
| Your call (`quantity`, `recipients[].amount`) | Display (`4.35`) | What a user types |
|
|
536
|
+
| `result.outputs` (RPC envelope) | Display (`4.35`) | `createrawtransaction` scales it itself |
|
|
537
|
+
| `result.createTransactionBuild` (`*Raw`, `*Sats`) | `bigint` (`435000000n`) | What the chain encodes |
|
|
538
|
+
|
|
539
|
+
The asset payload scale is always `10^8`, **independently of the asset's
|
|
540
|
+
`units`**: `units` limits divisibility and presentation, it is never a
|
|
541
|
+
multiplier. A quantity of `1.25` reaches the chain as `125000000n` whether the
|
|
542
|
+
asset has `units=2` or `units=8`.
|
|
543
|
+
|
|
544
|
+
Conversion goes through text rather than `Math.round(value * 1e8)`. That
|
|
545
|
+
multiplication is fine for ordinary magnitudes — `4.35 * 1e8` is
|
|
546
|
+
`434999999.99999994`, but `Math.round` recovers `435000000` — and it fails
|
|
547
|
+
silently in exactly two places:
|
|
548
|
+
|
|
549
|
+
- **more than eight decimals** are rounded away instead of rejected, so an
|
|
550
|
+
amount can vanish (`1e-9` → `0`) or shift (`1.123456789` → `112345679`);
|
|
551
|
+
- **past `Number.MAX_SAFE_INTEGER`** the scaled product drops bits:
|
|
552
|
+
`184467440.73709551` → `18446744073709552`, one unit off.
|
|
553
|
+
|
|
554
|
+
Both are reachable with values a wallet can hold. Here, an amount is rejected
|
|
555
|
+
rather than silently truncated when it has more than eight decimals, when the
|
|
556
|
+
asset's `units` cannot represent it, or when it exceeds the consensus ceiling
|
|
557
|
+
`MAX_MONEY` (`21000000000` units — the node's `MoneyRange`).
|
|
558
|
+
|
|
559
|
+
### Large amounts: pass a string
|
|
560
|
+
|
|
561
|
+
Above `MAX_SAFE_INTEGER / 1e8` (~`90071992.55`) a JavaScript `number` can no
|
|
562
|
+
longer name every 8-decimal value, so a **fractional** one is refused there and
|
|
563
|
+
the error names the string to use instead:
|
|
564
|
+
|
|
565
|
+
```javascript
|
|
566
|
+
await assets.createRootAsset({ assetName: 'BIG', quantity: 100000000.5, units: 1 });
|
|
567
|
+
// InvalidAmountError: ... pass it as a decimal string ("100000000.5") instead.
|
|
568
|
+
|
|
569
|
+
await assets.createRootAsset({ assetName: 'BIG', quantity: '100000000.5', units: 1 });
|
|
570
|
+
// works
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
A **safe integer** is still accepted however large it scales, so the documented
|
|
574
|
+
maximum supply `21000000000` keeps working as a number. Strings are exempt from
|
|
575
|
+
this precision guard — they carried their own digits — but not from any other
|
|
576
|
+
rule: sign, decimals, `units` divisibility and `MAX_MONEY` apply equally.
|
|
577
|
+
|
|
578
|
+
### Migration 1.4.x → 1.5.x → 2.0
|
|
579
|
+
|
|
580
|
+
| | 1.4.x | 1.5.x | 2.0 |
|
|
581
|
+
| --- | --- | --- | --- |
|
|
582
|
+
| `localRawBuild` | only option | present, **deprecated** | removed |
|
|
583
|
+
| `createTransactionBuild` | — | present, canonical | only option |
|
|
584
|
+
| Transfers via `createFromOperation` | rejected (`TRANSFER` is not a discriminant) | work | work |
|
|
585
|
+
| `*Raw` fields | display values | protocol integers | protocol integers |
|
|
586
|
+
| `toSatoshis(amount, units)` | returns the display amount | unchanged, deprecated | removed |
|
|
587
|
+
|
|
588
|
+
`localRawBuild` keeps its exact 1.4.x shape through the whole 1.x line, so no
|
|
589
|
+
consumer has to move on this release. New integrations should read
|
|
590
|
+
`createTransactionBuild` only.
|
|
591
|
+
|
|
592
|
+
### Serializer version
|
|
593
|
+
|
|
594
|
+
The canonical contract needs `@neuraiproject/neurai-create-transaction`
|
|
595
|
+
**>= 0.8.0**. Two of its fixes are load-bearing here:
|
|
596
|
+
|
|
597
|
+
- global `FREEZE_ASSET` / `UNFREEZE_ASSET` encode the restriction flag as
|
|
598
|
+
`1`/`0` (0.7.0 emitted `3`/`2`, which the node rejected with
|
|
599
|
+
`bad-txns-null-data-flag-must-be-0-or-1`, so those two discriminants could
|
|
600
|
+
not reach a mempool);
|
|
601
|
+
- a reissue that omits `units` encodes "keep the current units" (`0xff`), which
|
|
602
|
+
is what this library relies on — see below.
|
|
603
|
+
|
|
604
|
+
### Reissue never changes an asset's units
|
|
605
|
+
|
|
606
|
+
There is no API here to change the precision of an existing asset, so a reissue
|
|
607
|
+
build deliberately **omits** `units`, which the serializer encodes as `0xff`
|
|
608
|
+
("keep"). Echoing the value read from `getassetdata` would instead say "set
|
|
609
|
+
units to N", and a stale read — the asset reissued to a higher precision
|
|
610
|
+
between the read and the broadcast — would ask the node to lower them, which it
|
|
611
|
+
rejects with `unit must be larger than current unit selection`.
|
|
612
|
+
|
|
613
|
+
The value read from the chain is still used, to check that the requested
|
|
614
|
+
`quantity` fits the asset's precision.
|
|
615
|
+
|
|
616
|
+
One consequence worth knowing: the **node-built** `rawTx` cannot reissue an
|
|
617
|
+
asset whose `units` are above zero at all. `createrawtransaction`'s `reissue`
|
|
618
|
+
object has no field for units and the node fills in `0`, so it refuses. That is
|
|
619
|
+
a limitation of the RPC interface, not of the operation — build those offline
|
|
620
|
+
with `createFromOperation(result.createTransactionBuild)`. The error message
|
|
621
|
+
says so when you hit it.
|
|
622
|
+
|
|
494
623
|
## Owner Tokens - IMPORTANT
|
|
495
624
|
|
|
496
625
|
When you create an asset, an **owner token** is automatically generated (e.g., `MYTOKEN!`).
|
|
@@ -539,18 +668,43 @@ Ravencoin-inherited `rvn` to `xna` at an activation height per network
|
|
|
539
668
|
|
|
540
669
|
- Transactions built **through the node** (`createrawtransaction`) need
|
|
541
670
|
nothing: the node stamps the marker itself.
|
|
542
|
-
-
|
|
543
|
-
|
|
544
|
-
|
|
671
|
+
- Locally built transactions carry `params.assetMarker` in both
|
|
672
|
+
`createTransactionBuild` and the deprecated `localRawBuild`. Builders
|
|
673
|
+
resolve it **once per build**:
|
|
545
674
|
1. `params.assetMarker` / `config.assetMarker` if you set it (`'rvn'` |
|
|
546
675
|
`'xna'` — offline builds or tests);
|
|
547
676
|
2. otherwise the node's `getblockchaininfo.asset_marker` (node commit
|
|
548
677
|
`347362b` or later);
|
|
549
|
-
3. `'rvn'` when the node predates that field
|
|
550
|
-
|
|
678
|
+
3. `'rvn'` when the node predates that field — which matches what such a
|
|
679
|
+
node enforces.
|
|
551
680
|
|
|
552
681
|
No height tables and no network inference: the node (or you) decides.
|
|
553
682
|
|
|
683
|
+
### Failure policy (`assetMarkerPolicy`, 1.5.0+)
|
|
684
|
+
|
|
685
|
+
Step 3 above covers a node that *answers* without the field. A node that does
|
|
686
|
+
not answer at all is a different situation, and `assetMarkerPolicy` decides it:
|
|
687
|
+
|
|
688
|
+
```javascript
|
|
689
|
+
const assets = new NeuraiAssets(rpc, {
|
|
690
|
+
network: 'xna-test',
|
|
691
|
+
addresses: [...],
|
|
692
|
+
assetMarkerPolicy: 'strict' // default: 'legacy-fallback'
|
|
693
|
+
});
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
| Policy | `getblockchaininfo` fails | Field absent/null | Unknown value |
|
|
697
|
+
| --- | --- | --- | --- |
|
|
698
|
+
| `legacy-fallback` (default in 1.x) | resolves `'rvn'` | resolves `'rvn'` | throws |
|
|
699
|
+
| `strict` | **throws** | resolves `'rvn'` | throws |
|
|
700
|
+
|
|
701
|
+
Use `strict` in a connected wallet on a post-NIP-040 chain: guessing `'rvn'`
|
|
702
|
+
there builds a transaction the node rejects with
|
|
703
|
+
`bad-txns-legacy-asset-marker-after-nip040`, so "the node did not answer" must
|
|
704
|
+
not silently become "the node said rvn". The rejection propagates out of the
|
|
705
|
+
build — you never receive a partial result — and the node is queried only once,
|
|
706
|
+
whether the query succeeds or fails.
|
|
707
|
+
|
|
554
708
|
## Validations
|
|
555
709
|
|
|
556
710
|
The library validates client-side:
|
|
@@ -739,11 +893,15 @@ are `xna` and `xna-test`; `xna-pq` and `xna-pq-test` remain available as compati
|
|
|
739
893
|
|
|
740
894
|
## Fee estimation (PQ-aware)
|
|
741
895
|
|
|
742
|
-
Asset transactions are usually built with one or two XNA inputs plus, depending on the operation, an owner-token or qualifier UTXO.
|
|
896
|
+
Asset transactions are usually built with one or two XNA inputs plus, depending on the operation, an owner-token or qualifier UTXO. Since `1.5.0` the XNA side is funded by a loop that selects inputs, recomputes the fee from the *real* (PQ-aware) descriptors of the full input set, and repeats until the funds cover burn + fee. Every round excludes the outpoints it already holds, so a transaction never spends the same outpoint twice and the fee always accounts for every input it pays for.
|
|
743
897
|
|
|
744
|
-
|
|
898
|
+
Running out of funds raises `InsufficientFundsError` rather than returning an underfunded build. That includes a case worth knowing about: each PQ input costs about `0.0147 XNA` in fee, so a UTXO worth less than that makes the shortfall *worse*, and a wallet fragmented into such pieces cannot fund a PQ transaction at all.
|
|
745
899
|
|
|
746
|
-
|
|
900
|
+
All estimates share a single `estimatesmartfee` lookup. The fee rate is stable for the lifetime of one build, so it is fetched on the first estimate and cached on the builder instance.
|
|
901
|
+
|
|
902
|
+
Estimates use the helpers in [`src/utils/feeSizing.js`](src/utils/feeSizing.js) and distinguish PQ AuthScript inputs/outputs from legacy P2PKH ones. PQ inputs spend ~977 vbytes vs ~148 for legacy — without this distinction, transactions built from PQ addresses fall under the node's `min relay fee` and are rejected with `code -26: min relay fee not met`.
|
|
903
|
+
|
|
904
|
+
Outputs that carry an asset payload are sized as such, not as bare P2PKH outputs. An asset output is `<destination> OP_XNA_ASSET <pushdata payload> OP_DROP`, which adds roughly 20-60 bytes; ignoring that under-counts a transaction by a few percent, and that is enough to fall below the floor whenever the node's fee rate sits close to its minimum relay fee.
|
|
747
905
|
|
|
748
906
|
You should not need to call these helpers directly; they are wired into every builder. They are documented here so you can audit the fee math or use the same constants if you compose transactions outside the standard builder flow.
|
|
749
907
|
|
|
@@ -767,6 +925,12 @@ estimateInputVbytes({ address: 'nq1…' }); // 977
|
|
|
767
925
|
estimateInputVbytes({ address: 'mgRYHdMq…' }); // 148
|
|
768
926
|
estimateOutputBytes('tnq1…'); // 43
|
|
769
927
|
|
|
928
|
+
// Asset outputs declare their payload: kind is 'transfer' (default),
|
|
929
|
+
// 'owner', 'issue' or 'reissue'.
|
|
930
|
+
estimateOutputBytes({ address: 't7pv…', assetName: 'ROOTX' }); // 55
|
|
931
|
+
estimateOutputBytes({ address: 't7pv…', assetName: 'ROOTX!', kind: 'owner' }); // 48
|
|
932
|
+
estimateOutputBytes({ address: 't7pv…', assetName: 'ROOTX', kind: 'issue' }); // 58
|
|
933
|
+
|
|
770
934
|
const vbytes = estimateTransactionVbytes(
|
|
771
935
|
[{ script: '5120…' }, { address: 'mgRYHdMq…' }], // 1 PQ + 1 legacy input
|
|
772
936
|
['nq1qchange…', 'mgRYHdMqburn…'], // 1 PQ + 1 legacy output
|