npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@neuraiproject/neurai-jswallet

v0.14.2

Published

Neurai JavaScript wallet library

Readme

neurai-jswallet

Non-custodial Neurai wallet library for JavaScript and TypeScript.

By default it talks to the Neurai blockchain through public RPC services at https://rpc-main.neurai.org/rpc (mainnet) and https://rpc-testnet.neurai.org/rpc (testnet). You can point it at any RPC endpoint you control — see Run your own blockchain node.

Status: EXPERIMENTAL. Test thoroughly before using on mainnet.

What's inside

neurai-jswallet is a thin wallet shell that wires together the Neurai SDK stack. It owns key management, address scanning, UTXO discovery and signing, delegates transaction construction and asset operations to dedicated libraries, and re-exports the low-level script primitives so callers don't need extra installs:

| Concern | Library | |---|---| | Mnemonic / HD-key derivation (legacy + PQ) | @neuraiproject/neurai-key | | Raw transaction builders (payments, transfers, issue, reissue, freeze, tag) | @neuraiproject/neurai-create-transaction | | Asset orchestration & queries | @neuraiproject/neurai-assets | | Script primitives (covenants, multisig, AuthScript, OP_RETURN, P2SH/P2WSH...) | @neuraiproject/neurai-scripts | | Transaction signing (legacy ECDSA + ML-DSA-44 PQ) | @neuraiproject/neurai-sign-transaction | | RPC client | @neuraiproject/neurai-rpc |

Supported networks: xna, xna-test, xna-legacy, xna-legacy-test, xna-pq (post-quantum mainnet), xna-pq-test (post-quantum testnet).

Install

npm install @neuraiproject/neurai-jswallet

Build outputs

The package ships three flavours so it can be consumed from any environment:

| File | Format | Use | |---|---|---| | dist/index.cjs | CommonJS | Node, bundlers (require) | | dist/index.js | ESM | Node, bundlers (import) | | dist/browser.js | ESM (deps inlined) | Modern browsers | | dist/NeuraiJsWallet.global.js | IIFE | Drop in a <script> tag — exposes window.NeuraiJsWallet |

Examples below use ESM (.mjs).

Quick start

import NeuraiWallet, { generateMnemonic } from "@neuraiproject/neurai-jswallet";

// Create a brand-new wallet
const mnemonic = generateMnemonic();
const wallet = await NeuraiWallet.createInstance({
  mnemonic,
  network: "xna-test",
});

console.log(mnemonic);
console.log(await wallet.getBalance());

// Or restore from an existing mnemonic
const restored = await NeuraiWallet.createInstance({
  mnemonic: "horse sort develop lab chest talk gift damp session sun festival squirrel",
  network: "xna-test",
});

Mnemonic utilities

Top-level helpers re-exported from @neuraiproject/neurai-key. Useful before a wallet instance exists (creating, restoring, validating the seed):

import {
  generateMnemonic,
  isMnemonicValid,
  entropyToMnemonic,
} from "@neuraiproject/neurai-jswallet";

const mnemonic = generateMnemonic();          // 12 words, fresh entropy
isMnemonicValid(mnemonic);                    // true / false
entropyToMnemonic("00112233445566778899aabbccddeeff"); // hex → words

The same names are exposed on globalThis.NeuraiJsWallet when loading the IIFE bundle from a <script> tag, so a browser wallet can be built from a single script.

Full key namespace

For advanced consumers (offline derivation, manual HD key handling, address pair generation, AuthScript helpers...) the entire @neuraiproject/neurai-key surface is exposed under the key namespace:

import { key } from "@neuraiproject/neurai-jswallet";

// Address pair (sync, no RPC)
const { address, WIF } = key.getAddressPair("xna-test", mnemonic, 0, 0, passphrase);

// HD primitives
const hdKey = key.getHDKey("xna-test", mnemonic, passphrase);
const coin = key.getCoinType("xna-test");
const derived = key.getAddressByPath("xna-test", hdKey, "m/44'/175'/0'/0/0");

// PQ-HD primitives
const pqHd = key.getPQHDKey("xna-pq-test", mnemonic, passphrase);
const pqAddr = key.getPQAddressByPath("xna-pq-test", pqHd, "m_pq/100'/1'/0'/0'/0'");

Same access from a browser bundle: NeuraiJsWallet.key.getAddressPair(...).

Common operations

import NeuraiWallet from "@neuraiproject/neurai-jswallet";

const wallet = await NeuraiWallet.createInstance({
  mnemonic: "horse sort develop lab chest talk gift damp session sun festival squirrel",
  network: "xna-test",
});

wallet.getAddresses();             // string[] — all derived addresses
wallet.getAddressObjects();        // metadata: path, publicKey, privateKey, WIF, seedKey
await wallet.getBalance();         // base currency balance
await wallet.getAssets();          // asset balances
await wallet.getReceiveAddress();  // first unused external address
await wallet.getChangeAddress();   // first unused internal address
await wallet.getHistory();         // address deltas
await wallet.getMempool();         // mempool entries for this wallet's addresses
await wallet.getUTXOs();           // all UTXOs (XNA + assets)
await wallet.getAssetUTXOs();      // asset UTXOs only
wallet.getPrivateKeyByAddress(addr);

Send XNA and assets

import NeuraiWallet from "@neuraiproject/neurai-jswallet";

const wallet = await NeuraiWallet.createInstance({
  mnemonic: "mesh beef tuition ensure apart picture rabbit tomato ancient someone alter embrace",
  network: "xna-test",
});

// Send 100 XNA
const xnaTx = await wallet.send({
  toAddress: "tBkQUwLYgNuQysgaqYH6F75UiNvcsA5Wmy",
  amount: 100,
});
console.log("XNA tx:", xnaTx.transactionId);

// Send 313 BUTTER tokens
const assetTx = await wallet.send({
  assetName: "BUTTER",
  amount: 313,
  toAddress: "tBkQUwLYgNuQysgaqYH6F75UiNvcsA5Wmy",
});
console.log("Asset tx:", assetTx.transactionId);

Drain the wallet — sendMax

Pass sendMax: true to send the entire base-currency (XNA) balance to a single recipient. The wallet ends at exactly 0 — no leftover dust, no manual fee inflation. Internally the size is estimated without a change output and the amount is computed as balance − fee in satoshis (so there is no IEEE-754 drift). amount is ignored and may be omitted.

const result = await wallet.send({
  toAddress: "tBkQUwLYgNuQysgaqYH6F75UiNvcsA5Wmy",
  sendMax: true,
});
console.log(result.transactionId);
console.log(result.debug.sentMax);    // true
console.log(result.debug.fee);        // actual fee paid
console.log(result.debug.amount);     // amount that reached the recipient

Restrictions: sendMax only works for the base currency (XNA) and a single recipient. Combining it with assetName or sendMany throws a ValidationError.

Sub-dust change is absorbed into the fee. For any send (with or without sendMax), if the change output would be under the network's 546-sat dust threshold the wallet drops the change output entirely and the residue becomes part of the miner fee. This is the only way for the network to accept the transaction — sub-dust outputs are rejected. The actual absorbed sats are reported as result.debug.dustAbsorbedSats for transparency.

Send to many recipients

const result = await wallet.sendMany({
  assetName: "BUTTER",
  outputs: {
    tBkQUwLYgNuQysgaqYH6F75UiNvcsA5Wmy: 1,
    tD9hhanNRywGzn2mCwLkmrf3USWAD5REMA: 2,
  },
});
console.log(result.transactionId);

Build without broadcasting

send and sendMany build, sign and broadcast. To inspect a transaction (fee, change, raw hex) before broadcasting, use the create* variants:

const draft = await wallet.createTransaction({
  toAddress: "tBkQUwLYgNuQysgaqYH6F75UiNvcsA5Wmy",
  amount: 1,
});

console.log(draft.debug.fee);
console.log(draft.debug.signedTransaction);   // hex, ready to broadcast
console.log(draft.debug.rawUnsignedTransaction);

// Broadcast manually when ready
await wallet.sendRawTransaction(draft.debug.signedTransaction);

createSendManyTransaction({ outputs, assetName }) does the same for multi-output transactions.

Forced UTXOs and change addresses

send/sendMany/createTransaction accept optional fine-grained controls:

await wallet.sendMany({
  assetName: "BUTTER",
  outputs: { tBkQUwLYgNuQysgaqYH6F75UiNvcsA5Wmy: 1 },
  forcedUTXOs: [
    {
      utxo: { address, assetName, txid, outputIndex, script, satoshis, value },
      address,
      privateKey: addressObject.WIF, // or seedKey object for PQ
    },
  ],
  forcedChangeAddressBaseCurrency: "tKevLHxnRC4srYDP6vGrYPRESkL9p4wd5Y",
  forcedChangeAddressAssets:       "tL1vjZj1KYd1FuAcCv4KWQPYMsJxA2rJoH",
});

Asset operations

The wallet exposes a full asset toolkit through wallet.assets — and as shortcuts directly on the wallet — backed by @neuraiproject/neurai-assets.

Every method accepts these common options:

| Option | Default | Meaning | |---|---|---| | broadcast | true | Sign and submit. Set false to inspect only. | | toAddress | wallet.getReceiveAddress() | Recipient of the issued / reissued asset | | changeAddress | wallet.getChangeAddress() | XNA change destination |

All return an AssetOpResult:

{
  transactionId: string | null,
  rawTx: string,
  signedTransaction: string,
  fee: number,
  burnAmount: number,
  changeAddress: string | null,
  changeAmount: number | null,
  inputs: Array<{ txid: string; vout: number; address: string }>,
  outputs: Array<Record<string, unknown>>,
}

How an asset op talks to the node

Internally, every asset op runs in two phases — build (@neuraiproject/neurai-assets selects UTXOs and produces an unsigned rawTx) and sign + broadcast (this wallet derives the witness and submits). To keep round-trip count low against remote / public RPC endpoints, the wallet:

  • reuses the UTXOs that the build phase already fetched (result.utxos) instead of re-querying getaddressutxos / getaddressmempool before signing — saves 4-5 RPC calls per op.
  • relies on @neuraiproject/neurai-assets ≥ 1.3.2, which (a) caches estimatesmartfee for the lifetime of a single build — saves 1 more RPC, and (b) sends asset_quantity as the user-facing display value so the daemon's own AmountFromValue does the 10⁸ scaling exactly once (1.2.2/1.3.1 pre-multiplied locally and the chain inflated the result by another factor of 10⁸ — fixed in 1.3.2).

If result.utxos ever lacks the script field (older neurai-assets releases or unusual code paths), the wallet falls back to a fresh fetch automatically — slower path, but always correct.

Net effect: an issueRoot from a PQ address went from ~12 RPC round trips to ~5 in this version. Latency improvement scales linearly with your RPC RTT.

Issue assets

// ROOT asset
await wallet.issueRoot({
  assetName: "MYTOKEN",
  quantity: 1_000_000,
  units: 2,            // 0–8 decimals
  reissuable: true,
  ipfsHash: undefined, // optional metadata
});

// SUB asset (requires owning the parent)
await wallet.issueSub({
  assetName: "MYTOKEN/SUB",
  quantity: 100,
  units: 0,
  reissuable: false,
});

// UNIQUE assets (NFTs)
await wallet.issueUnique({
  rootName: "MYTOKEN",
  assetTags: ["#001", "#002", "#003"],
  ipfsHashes: [hash1, hash2, hash3],   // optional, one per tag
});

// QUALIFIER (KYC tag)
await wallet.issueQualifier({
  assetName: "#KYC",
  quantity: 10,
});

// RESTRICTED asset (compliance / verifier)
await wallet.issueRestricted({
  assetName: "$STOCK",
  quantity: 1_000_000,
  verifierString: "#KYC",
  units: 0,
  reissuable: true,
});

// DEPIN (soulbound)
await wallet.issueDepin({
  assetName: "DEVICE001",
  quantity: 1,
  ipfsHash: "Qm...",
});

Reissue

quantity is the additional supply in user-facing display units — exactly what the user types in the UI. Decimals (units) cannot be changed on reissue; they are inherited from the asset's on-chain metadata, so don't pass them here.

await wallet.reissue({
  assetName: "MYTOKEN",
  quantity: 500_000,        // additional supply (display tokens)
  reissuable: true,         // false to permanently lock supply
  ipfsHash: undefined,      // optional new IPFS metadata
});

await wallet.reissueRestricted({
  assetName: "$STOCK",
  quantity: 250_000,
  verifierString: "#KYC",   // optional new verifier expression
});

Tag / untag (qualifier)

await wallet.tagAddresses({
  qualifierName: "#KYC",
  targetAddresses: [
    "tBkQUwLYgNuQysgaqYH6F75UiNvcsA5Wmy",
    "tD9hhanNRywGzn2mCwLkmrf3USWAD5REMA",
  ],
});

await wallet.untagAddresses({
  qualifierName: "#KYC",
  targetAddresses: ["tBkQUwLYgNuQysgaqYH6F75UiNvcsA5Wmy"],
});

Freeze (restricted assets)

// Freeze specific addresses for a restricted asset
await wallet.freezeAddresses({
  assetName: "$STOCK",
  targetAddresses: ["tBkQUwLYgNuQysgaqYH6F75UiNvcsA5Wmy"],
});

await wallet.unfreezeAddresses({
  assetName: "$STOCK",
  targetAddresses: ["tBkQUwLYgNuQysgaqYH6F75UiNvcsA5Wmy"],
});

// Freeze the whole asset globally
await wallet.freezeAssetGlobally({ assetName: "$STOCK" });
await wallet.unfreezeAssetGlobally({ assetName: "$STOCK" });

Asset queries

Read-only blockchain queries are exposed through wallet.assets.queries:

const data = await wallet.assets.queries.getAssetData("BUTTER");
const exists = await wallet.assets.queries.assetExists("BUTTER");
const all = await wallet.assets.queries.listAssets("MY*", true, 100, 0);
const mine = await wallet.assets.queries.listMyAssets();
const holders = await wallet.assets.queries.listAddressesByAsset("BUTTER");
const balances = await wallet.assets.queries.listAssetBalancesByAddress(addr);

// Qualifiers / restrictions
await wallet.assets.queries.checkAddressTag(addr, "#KYC");
await wallet.assets.queries.listTagsForAddress(addr);
await wallet.assets.queries.checkAddressRestriction(addr, "$STOCK");
await wallet.assets.queries.isAddressFrozen(addr, "$STOCK");
await wallet.assets.queries.checkGlobalRestriction("$STOCK");
await wallet.assets.queries.getVerifierString("$STOCK");

// DEPIN
await wallet.assets.queries.listDepinHolders("DEVICE001");
await wallet.assets.queries.checkDepinValidity("DEVICE001", addr);

Low-level script primitives

@neuraiproject/neurai-scripts is re-exported under the scripts namespace so you don't need a second npm install to assemble custom scripts (covenants, multisig, OP_RETURN, AuthScript, P2WSH/P2SH...).

import { scripts } from "@neuraiproject/neurai-jswallet";

// Partial-fill covenants (legacy + PQ)
const orderHex = scripts.buildPartialFillScriptHex({ ... });
const cancelSig = scripts.buildCancelScriptSig({ ... });
const orderHexPQ = scripts.buildPartialFillScriptPQHex({ ... });

// Standard scripts
const p2pkh = scripts.encodeP2PKHScriptPubKey(pubKeyHash);
const redeem = scripts.encodeMultisigRedeemScript({ m: 2, pubkeys: [...] });
const p2sh = scripts.encodeP2SHScriptPubKey(scriptHash);
const opReturn = scripts.encodeNullDataScript(payload);

// Core primitives
const builder = new scripts.ScriptBuilder()
  .opcode(scripts.opcodes.OP_DUP)
  .opcode(scripts.opcodes.OP_HASH160)
  .pushBytes(pubKeyHash);

See @neuraiproject/neurai-scripts for the full API.

Sweep an external private key

Move every UTXO held by an arbitrary WIF private key into your wallet. Only legacy networks are supported — sweeping PQ keys is not allowed.

const result = await wallet.sweep("KxA0...WIF...", true /* broadcast */);
console.log(result.transactionId);

Post-quantum wallets (PQ)

xna-pq and xna-pq-test use NIP-022 PQ-HD derivation (every path level hardened) and ML-DSA-44 signatures. Address format is bech32m starting with nq1 / tnq1.

const pq = await NeuraiWallet.createInstance({
  mnemonic: "result pact model attract result puzzle final boss private educate luggage era",
  network: "xna-pq-test",
  offlineMode: true, // skip RPC discovery — useful when the node has not yet indexed PQ
});

const addr = await pq.getReceiveAddress();    // tnq1...
const obj = pq.getAddressObjects()[0];
console.log(obj.seedKey);                     // hex ML-DSA-44 seed

The same wallet.send, wallet.createTransaction, asset operations etc. all work for PQ wallets — the signer detects the address type and produces a ML-DSA-44 witness.

Passphrase support (BIP39 25th word)

An optional passphrase derives a completely different set of addresses from the same mnemonic. If you lose the passphrase you cannot recover the wallet even with the mnemonic.

const wallet = await NeuraiWallet.createInstance({
  mnemonic: "your twelve word mnemonic phrase here",
  network: "xna-test",
  passphrase: "my secret passphrase",   // omit or "" for the default wallet
});

Use cases:

  • Extra layer of security on top of the mnemonic
  • Multiple wallets from a single seed
  • Plausible deniability (different passphrases → different wallets)

Configuration

interface IOptions {
  mnemonic: string;
  network?: "xna" | "xna-test" | "xna-legacy" | "xna-legacy-test" | "xna-pq" | "xna-pq-test";
  passphrase?: string;
  rpc_url?: string;
  rpc_username?: string;
  rpc_password?: string;
  minAmountOfAddresses?: number;   // pre-derive at least N addresses on init
  offlineMode?: boolean;           // skip every RPC call during init/address selection
}

See dist/entries/index.d.ts for the full TypeScript surface.

Use a custom RPC

const wallet = await NeuraiWallet.createInstance({
  mnemonic,
  network: "xna-test",
  rpc_url:      "http://localhost:8888",
  rpc_username: "myuser",
  rpc_password: "mypassword",
});

Run your own blockchain node

To expose your own node over HTTPS:

Advanced — direct RPC access

Every wallet exposes the underlying RPC function as wallet.rpc:

const wallet = await NeuraiWallet.createInstance({ mnemonic, network: "xna-test" });

const blockhash = await wallet.rpc("getbestblockhash", []);
const block = await wallet.rpc("getblock", [blockhash]);
console.log(block);

Use from a browser via <script>

A single IIFE bundle exposes everything you need (wallet API, mnemonic helpers, script primitives, asset toolkit) on window.NeuraiJsWallet. No extra scripts required:

<script src="https://unpkg.com/@neuraiproject/neurai-jswallet/dist/NeuraiJsWallet.global.js"></script>
<script type="module">
  const mnemonic = NeuraiJsWallet.generateMnemonic();
  const wallet = await NeuraiJsWallet.createInstance({
    mnemonic,
    network: "xna-test",
    offlineMode: true,
  });
  console.log(mnemonic);
  console.log(await wallet.getReceiveAddress());
</script>

License

MIT