@openrai/nano-core
v3.6.0
Published
Protocol engine for Nano integration ecosystem
Readme
@openrai/nano-core provides the protocol-level pieces needed to integrate Nano without rebuilding address validation, exact amounts, endpoint normalization, authenticated RPC failover, or Proof-of-Work routing. It is for applications that own their integration policy; it does not provide wallet storage, confirmation tracking, or hosted custody.
Installation
pnpm add @openrai/nano-core nano-rspow-node
# Browser: pnpm add @openrai/nano-core nano-rspow-webChoose one runtime facade: /node uses the native nano-rspow-node binding; /web uses nano-rspow-web (WASM/WebGPU). The root package is runtime-neutral and accepts a caller-supplied PowEngine.
Quick Start
1. Minimal Client
hydrateWallet() creates an in-memory signer for one seed index. send() submits a real Nano send block. The returned hash means the RPC accepted the block; it does not mean the block is confirmed.
import { NanoAddress, NanoAmount, NanoClient } from '@openrai/nano-core/node';
// import { NanoAddress, NanoAmount, NanoClient } from '@openrai/nano-core/web';
const client = NanoClient.initialize();
const wallet = client.hydrateWallet(process.env.NANO_SEED!, { index: 0 });
const destination = NanoAddress.parse(
'nano_3arg3asgtigae3xckabaaewkx3bzsh7nwz7jkmjos79ihyaxwphhm6qgjps4',
);
const amount = NanoAmount.fromNano('1.25');
console.log(`Sending from ${wallet.address}`);
const hash = await wallet.send(destination, amount);
console.log(`Submitted block: ${hash}`);Keep the seed outside source control. The source account must already be opened and have enough confirmed balance. The wallet does not store the seed, discover other indexes, receive pending funds, or wait for confirmation.
2. Endpoint Observation
The client constructs pools without network requests. After an RPC, WebSocket, or configured remote-work request succeeds, inspect the canonical secret-free endpoint URL:
const unsubscribe = client.onEndpointChange((event) => {
console.log(event.kind, event.status, event.activeUrl);
});
console.log(client.getActiveEndpoints());
// { rpc?: string, ws?: string, work?: string }
unsubscribe();3. Generic RPC Calls
rpcPool.postJson() exposes the same normalized endpoint, auth, backoff, and failover behavior used by the wallet sender:
const balance = await client.rpcPool.postJson<{ balance: string; pending: string }>({
action: 'account_balance',
account: wallet.address.toString(),
});
console.log({ confirmedRaw: balance.balance, receivableRaw: balance.pending });4. Work Routing
The Node facade follows nano-rspow-node's persisted local-work recommendation. The web facade exposes asynchronous recommendLocalPow(reprobe?) over its local WASM/WebGPU engine; pass true to force a fresh browser capability evaluation. Both validate returned remote nonces locally. Configure explicit work endpoints or NANO_WORK_URL when your application selects remote work; the work pool uses the same auth and failover behavior as RPC.
const client = NanoClient.initialize({
rpc: ['https://rpc.example.com'],
work: [
'https://work-primary.example.com?api_key=replace-me',
'https://work-secondary.example.com?api_key=replace-me',
],
workRouting: {
selectRoute: () => 'remote',
onRemoteFailure: 'error',
},
});Remote selection without a configured work endpoint fails instead of silently changing policy. Applications with their own work selection can inject their own engine and route:
import { NanoClient, WorkProvider, type PowEngine } from '@openrai/nano-core';
declare const powEngine: PowEngine;
const client = NanoClient.initialize({
workProvider: WorkProvider.local({ localEngine: powEngine, localTimeoutMs: 60_000 }),
});Applications such as xno-skills, RaiFlow SDK, and nanosweeper retain their own approval, custody, signing, and submission policy. They use the root package with an injected PowEngine/WorkProvider and do not need to call hydrateWallet().
Naming a difficulty
WorkProvider accepts either a named level or a threshold you already hold as hex. workDifficultyToThreshold is the seam between the two:
import { WorkDifficulty, workDifficultyToThreshold } from '@openrai/nano-core';
WorkDifficulty.Send; // 'send'
workDifficultyToThreshold(WorkDifficulty.Send); // 'fffffff800000000'
workDifficultyToThreshold('fffffff800000000'); // same — hex passes throughCanonical levels are owned by @openrai/nano-pow-contract and resolved here, so this package and the engines cannot disagree about what a threshold is. Names are case-insensitive and surrounding whitespace is tolerated.
PowEngine stays deliberately in hex. Engines are runtime-neutral and should not need to know the network's vocabulary, which is why naming happens one layer up in the provider. For that reason workTypeToHex is deprecated: it takes nano-rspow-node's WorkType, which ties the difficulty vocabulary to one engine binding. It still works, and remains the right choice for the engine-specific LegacyWorkType / TestingWorkType presets.
5. Precision-Safe Primitives
NanoAddress.parse() verifies address checksums. NanoAmount accepts exact decimal strings and stores Nano values as raw integers without floating-point conversion.
const destination = NanoAddress.parse('nano_3arg3asgtigae3xckabaaewkx3bzsh7nwz7jkmjos79ihyaxwphhm6qgjps4');
const amount = NanoAmount.fromNano('0.000000000000000000000000000001');
console.log(amount.raw); // 1See transport/auth design for endpoint credential handling and audit behavior.
Release Flow
This pnpm workspace contains two independently versioned packages: @openrai/nano-core and @openrai/nano-pow-contract. A package is published only from its package-scoped tag; ordinary main pushes run CI and never attempt publication.
| Package | Tag | Version command | Workflow | npm workflow filename |
| --- | --- | --- | --- | --- |
| @openrai/nano-core | nano-core-v<version> | pnpm version:core patch | Publish Nano Core | release.yml |
| @openrai/nano-pow-contract | nano-pow-contract-v<version> | pnpm version:pow-contract patch | Publish Nano PoW Contract | publish-pow-contract.yml |
Before the first release, add an npm Trusted Publisher for each package. Use GitHub Actions from OpenRai/nano-core. Allow npm publish. npm matches the workflow filename, not its display name.
Release one package at a time from a clean Git worktree. The version command commits the selected version bump and creates its tag atomically. Choose the appropriate semver increment. Push main, then push only the created tag:
git push origin main
git push origin nano-core-v<version>For a contract release, push nano-pow-contract-v<version> instead. The tag version must match the selected package manifest. The tagged commit must be reachable from main. The matching workflow builds, tests, and publishes through npm Trusted Publisher. Never run pnpm publish locally.
Publish the contract before publishing a Nano Core version that depends on a new contract version. Confirm the contract version is in npm before releasing Nano Core:
pnpm view @openrai/nano-pow-contract versionThe contract normally remains unchanged and does not need a release for ordinary Nano Core releases.
