JavaScript SDK
There are two client libraries, both in the repository:
| Library | Where | Use it when |
|---|---|---|
dApp SDK (NeuroChain) | neurochain-web/src/sdk/neurochain.js | Your page holds the user's key (or you are scripting against your own wallet) |
Signer SDK (NeuroSigner) | packages/neurochain-signer-sdk | Your dApp runs on another site and the user signs in their NeuroChain wallet, in a pop-up |
Neither is published to npm yet; copy the module or build the package from the repository.
dApp SDK
A single ES module with no dependencies. Reads are plain fetch (browser or Node 18+); writes sign with ML-DSA-65 using the same crypto WASM as the wallet, so the node accepts the signatures.
import { NeuroChain, OP_TARGETS, toRaw } from './neurochain.js'
const nc = new NeuroChain({ gateway: 'http://localhost:3000' })| Option | Default | Meaning |
|---|---|---|
gateway | http://localhost:3000 | Gateway base URL |
crypto | — | An already-initialised crypto WASM module |
cryptoLoader | loads ../wasm/neuro_wasm_crypto.js | Async function returning the initialised module |
Amounts
Every value and amount you pass is human NRO ('1.5', 2, '0.000021'). The SDK converts to integer uNRO with toRaw(), so the amount you sign and the amount you send are always the same. For display, use the *_human fields the API returns.
Reads
| Method | Calls | Returns |
|---|---|---|
networkStatus() | GET /api/v1/network/status | Height, shards, validators |
shards() | GET /api/v1/shards | Per-shard heights |
account(addr) | GET /api/v1/accounts/:addr | balance, balance_human, nonce, … |
balance(addr) | via account | { raw, human, nonce } |
nextNonce(addr) | via account | Confirmed nonce + 1 |
validators() | GET /api/v1/validators | Validator list with stake and metrics |
blocks({ limit, offset }) | GET /api/v1/blocks | Latest blocks |
block(height) | GET /api/v1/blocks/:height | One block |
tx(hash) | GET /api/v1/transactions/:hash | One transaction |
contract(addr) / contracts({ limit, offset }) | GET /api/v1/contracts… | Contract metadata |
gasEstimate() | GET /api/v1/gas/estimate | base_fee and estimates |
contractQuery(addr, fn, args) | POST /api/v1/contracts/:addr/call with dry_run: true | Parsed return value; no transaction, no gas |
Wallet
const wallet = await nc.walletFromMnemonic('<24 words>')
wallet.address // neuro:<15 hex>.human
wallet.publicKey // ML-DSA-65 public key, hexThe private seed stays inside the wallet object; it is never exposed as a property.
Writes
// Transfer
await nc.transfer({ wallet, to: 'neuro:<address>.human', value: '1.5', gasPrice })
// Any transaction, optionally with call data
await nc.sendTransaction({ wallet, to, value, data, gasPrice })
// Contract call — JSON call data { function, args }
await nc.contractCall({ wallet, address: 'neuro:<10 hex>.app', fn: 'transfer', args: { … }, value, gasPrice })
// Deploy — a transaction to neuro:system.deploy
await nc.deployContract({ wallet, bytecodeHex, name, description, template, initArgs, gasPrice })Each write reads the next nonce, signs nc-tx-v2|from|to|value|nonce, and posts to POST /api/v1/transactions.
Set the gas price
The SDK defaults to gasPrice: 1 (deploys: 5). The node refuses a transaction priced below the current base fee, which rises under load. Read it first and pass gasPrice explicitly:
const { base_fee } = await nc.gasEstimate()
const gasPrice = Math.ceil(base_fee * 1.5)Signed operations
Staking, name registration and other system operations sign against a system target with a millisecond-timestamp nonce (see Wallet → Signed operations):
const op = nc.signOp(wallet, OP_TARGETS.name, 0) // { signature, public_key, nonce }
// merge op into the endpoint's request bodyOP_TARGETS has stake (neuro:system.stake) and name (neuro:system.name).
Known issues
Known issues in the current SDK
- Transfers with a non-zero value fail.
sendTransaction/transfersendvalueas a JSON string; the node reads only a number and refuses the request withMissing from/to/value. Calls and deploys with value 0 are not affected. Workaround — sign and post with a numeric value:jsconst nonce = await nc.nextNonce(wallet.address) const value = Number(toRaw('1.5')) // uNRO as a number const sig = wallet.sign(to, value, nonce) await fetch(GATEWAY + '/api/v1/transactions', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ from: wallet.address, to, value, nonce, gas_price, ...sig }) }) contractQueryreturns the raw string when the contract's reply is not clean JSON — NRC-20 replies end with a NUL byte, so you get'{"ok":true,"value":…}'as a string; strip\u0000and parse.stake()signs the amount converted to uNRO twice, so the node rejects the signature. Until it is fixed, build the request yourself:{ address, amount: toRaw(amount), ...nc.signOp(wallet, OP_TARGETS.stake, amount) }posted to/api/v1/staking/stake.staking(addr)requests/api/v1/staking/:addr, which the gateway does not serve; useGET /api/v1/staking/info/:address.contractCalladds acallerfield to the call JSON. Contracts ignore it — identity comes from the host — so it is harmless.
Crypto module
Signing needs neuro_wasm_crypto — the wallet's WASM build (neurochain-web/src/wasm/neuro_wasm_crypto.js and neuro_wasm_crypto_bg.wasm). It must export nc_keypair_from_words, nc_tx_message and nc_sign. If your page serves it elsewhere, pass a loader:
const nc = new NeuroChain({
gateway: 'http://localhost:3000',
cryptoLoader: async () => {
const mod = await import('/wasm/neuro_wasm_crypto.js')
await mod.default()
return mod
},
})Without the module, reads work and every write throws.
Signer SDK
For a dApp on another origin (an exchange, a game) that must not see the user's phrase. The user's NeuroChain web app opens in a pop-up, the user approves, and the signature comes back over postMessage.
import { NeuroSigner } from 'neurochain-signer-sdk'
const signer = new NeuroSigner({
signerUrl: 'http://localhost:8080/signer', // the wallet app's /signer page
allowedOrigins: ['http://localhost:8080'],
})
// Must run directly inside a click handler, or the browser blocks the pop-up
button.onclick = async () => {
const { address, publicKey } = await signer.connect()
const { signature, nonce } = await signer.signOperation({
to: 'neuro:system.dex',
value: 1000000, // uNRO
})
}| Method | Returns |
|---|---|
connect() | { address, publicKey } |
signOperation({ to, value, actor? }) | { signature, publicKey, nonce } — a signed operation for actor (default: the connected address) |
isConnected(), getAddress(), getPublicKey() | Connection state |
close() | Closes the pop-up and disconnects |
Errors carry a code: POPUP_BLOCKED, CONNECTION_TIMEOUT, SIGNATURE_REJECTED, INVALID_ORIGIN, NOT_CONNECTED. On POPUP_BLOCKED, show the user a direct link to the signer page.
Messages are accepted only from allowedOrigins; set it to your wallet app's origin.