xno-skills
v0.9.0
Published
XNO CLI - Interact with the Nano cryptocurrency
Downloads
1,067
Maintainers
Readme
xno-skills
A TypeScript library, executable, and MCP server, for interacting with the Nano (XNO) cryptocurrency. Generate wallets, convert units, validate addresses, and more.

AI Skills
This repository includes built-in skills for AI agents (Claude Code, Cursor, etc.). To install them:
npx skills add CasualSecurityInc/xno-skillsAvailable skills:
create-wallet: Wallet creation/import guidance (BIP39 default; legacy supported; safe--stdinworkflows).convert-units: High-precision unit conversion reference.generate-qr: Terminal-friendly Nano payment QR codes (address + optional amount).validate-address: Address format and checksum verification guide.check-balance: Check balance/pending via Nano node RPC.mcp-wallet: Usexno-mcpas a private “wallet” custody blackbox (addresses only; no seed leakage).request-payment: Request XNO from operator (payment request workflow with tracking, QR, receive, report).return-funds: Return XNO to sender (source attribution, ambiguity guards, safe refund).
MCP Server
This package includes a built-in Model Context Protocol (MCP) server that exposes Nano wallet functions as native tools for AI agents (like Claude Desktop or Cursor).
To use it, add the following to your MCP client configuration:
{
"mcpServers": {
"xno": {
"command": "npx",
"args": ["-y", "-p", "xno-skills@latest", "xno-mcp"]
}
}
}Exposed tools:
wallet_create/wallet_list/wallet_addresses: Named “wallets” (custody inside MCP; return addresses only).wallet_balance/wallet_probe_balances: Balance/pending checks for wallet accounts via RPC.wallet_receive/wallet_send: Receive pending blocks and send funds (sign + work +processvia RPC).config_get/config_set: Store defaults (RPC URL, work URL, timeouts, default representative; optional wallet persistence).wallet_set_allowance/wallet_get_allowance: Spending limits per wallet (per-tx cap, window budget, destination whitelist).wallet_history: Persistent transaction log (sends, receives, linked payment requests).payment_request_create/payment_request_status/payment_request_receive/payment_request_list: Payment request lifecycle.payment_request_refund: Safe refund with source attribution and ambiguity guards.generate_qr: Generate QR codes for Nano addresses/payment URIs.generate_wallet: Generate a wallet (default: BIP39 derivation).derive_address: Derive an address (supportsbip39+legacy, withautopreference).probe_mnemonic: Probe both derivations via RPC (helps resolve 24-word ambiguity).convert_units: Converts between XNO and raw units.validate_address: Validates address format and checksum.
Installation
npm install xno-skillsReleasing
See RELEASING.md.
MCP Client Setup (Codex, Claude, OpenCode, Gemini, VS Code)
All examples run the MCP server via npx (swap @latest for a pinned version if you prefer).
Codex
codex mcp add xno \
-c sandbox_mode="danger-full-access" \
-c 'sandbox_permissions=["network-access"]' \
-- npx -y -p xno-skills@latest xno-mcpClaude Desktop (claude_desktop_config.json)
Add via Claude Desktop: Settings -> Developer -> Edit Config.
{
"mcpServers": {
"xno": {
"command": "npx",
"args": ["-y", "-p", "xno-skills@latest", "xno-mcp"]
}
}
}OpenCode (opencode.jsonc)
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"xno": {
"type": "local",
"command": ["npx", "-y", "-p", "xno-skills@latest", "xno-mcp"],
"enabled": true
}
}
}Gemini CLI (settings.json)
{
"mcpServers": {
"xno": {
"command": "npx",
"args": ["-y", "-p", "xno-skills@latest", "xno-mcp"]
}
}
}VS Code (.vscode/mcp.json)
{
"servers": {
"xno": {
"type": "stdio",
"command": "npx",
"args": ["-y", "-p", "xno-skills@latest", "xno-mcp"]
}
}
}CLI Usage
Install globally or use with npx:
# Install globally
npm install -g xno-skills
# Or use with npx
npx xno-skills --helpWallet Commands
Security note: avoid pasting mnemonics/seeds into chat logs. Prefer --stdin (or --mnemonic-env) for import/probing commands.
Create a new wallet
# Default: BIP39 derivation
xno-skills wallet create
# Choose legacy derivation (24-word “seed phrase” style)
xno-skills wallet create --format legacy
# Control BIP39 word count / passphrase / index
xno-skills wallet create --words 12
xno-skills wallet create --passphrase "optional passphrase"
xno-skills wallet create --index 0
# JSON output
xno-skills wallet create --jsonRestore from mnemonic
# Safer import via stdin (recommended)
echo "word1 word2 ... word24" | xno-skills wallet from-mnemonic --stdin --json
# JSON output
xno-skills wallet from-mnemonic --stdin --jsonProbe mnemonic ambiguity (24-word)
If you have a Nano RPC endpoint, you can probe the first few indexes for both derivations and see which accounts are opened / have balances:
export NANO_RPC_URL="http://127.0.0.1:7076"
echo "word1 word2 ... word24" | xno-skills wallet probe-mnemonic --stdin --jsonConvert Units
# Convert XNO to raw
xno-skills convert 1.5 xno --to raw
# Convert raw to XNO
xno-skills convert 1500000000000000000000000000000 raw --to xno
# Convert between units
xno-skills convert 1 mnano --to knano
# JSON output
xno-skills convert 1 xno --to raw --jsonSupported units:
xnoornano- Nano (10^30 raw)knano- Kilo-nano (10^27 raw)mnano- Mega-nano (10^24 raw)raw- Base unit
Generate QR Codes
# QR code for address
xno-skills qr nano_1abc123...
# QR code with amount
xno-skills qr nano_1abc123... --amount 1.5
# JSON output
xno-skills qr nano_1abc123... --jsonValidate Addresses
xno-skills validate nano_1abc123...RPC (balance/pending)
export NANO_RPC_URL="http://127.0.0.1:7076"
xno-skills rpc account-balance nano_1abc123... --json --xnoAPI Reference
Seed Generation
generateMnemonic(wordCount = 24): string
Generate a BIP39 mnemonic (12/15/18/21/24 words).
import { generateMnemonic } from 'xno-skills';
const mnemonic = generateMnemonic(24);generateSeed(): string
Generate a cryptographically secure 32-byte seed (256 bits).
import { generateSeed } from 'xno-skills';
const seed = generateSeed();
// Returns: "0123456789abcdef..." (64 hex characters)seedToMnemonic(seed: string): string
Convert a hex-encoded seed to a BIP39 mnemonic phrase.
import { seedToMnemonic } from 'xno-skills';
const mnemonic = seedToMnemonic(seed);
// Returns: "word1 word2 word3 ... word24"mnemonicToSeed(mnemonic: string): string
Convert a BIP39 mnemonic phrase back to its underlying entropy (hex).
Note: this is not the BIP39 PBKDF2 “seed”; it’s the raw entropy the mnemonic encodes.
import { mnemonicToSeed } from 'xno-skills';
const seed = mnemonicToSeed(mnemonic);
// Returns: "0123456789abcdef..." (64 hex characters)mnemonicToBIP39Seed(mnemonic: string, passphrase?: string): string
Convert a BIP39 mnemonic (+ optional passphrase) to the PBKDF2 “seed” hex used for HD derivation.
validateMnemonic(mnemonic: string): boolean
Validate a BIP39 mnemonic phrase.
import { validateMnemonic } from 'xno-skills';
const isValid = validateMnemonic(mnemonic);
// Returns: true or falseLegacy Address Derivation
deriveAddressLegacy(seed: string, index: number): LegacyAddressResult
Derive a Nano address from a seed using the legacy derivation method.
import { deriveAddressLegacy } from 'xno-skills';
const result = deriveAddressLegacy(seed, 0);
// Returns: { address, privateKey, publicKey }derivePrivateKeyLegacy(seed: string, index: number): string
Derive a private key from a seed at the specified index.
import { derivePrivateKeyLegacy } from 'xno-skills';
const privateKey = derivePrivateKeyLegacy(seed, 0);
// Returns: "0123456789abcdef..." (64 hex characters)derivePublicKeyLegacy(privateKey: string): string
Derive a public key from a private key.
import { derivePublicKeyLegacy } from 'xno-skills';
const publicKey = derivePublicKeyLegacy(privateKey);
// Returns: "0123456789abcdef..." (64 hex characters)publicKeyToAddress(publicKey: string): string
Convert a public key to a Nano address.
import { publicKeyToAddress } from 'xno-skills';
const address = publicKeyToAddress(publicKey);
// Returns: "nano_1abc123..."BIP44 Address Derivation
deriveAddressBIP44(mnemonic: string, index: number, passphrase?: string): BIP44AddressResult
Derive a Nano address from a mnemonic using BIP44 path m/44'/165'/[index]'.
import { deriveAddressBIP44 } from 'xno-skills';
const result = deriveAddressBIP44(mnemonic, 0);
// Returns: { address, privateKey, publicKey }
// With optional passphrase
const result = deriveAddressBIP44(mnemonic, 0, 'my-passphrase');derivePrivateKeyBIP44(mnemonic: string, index: number, passphrase?: string): string
Derive a private key from a mnemonic using BIP44.
import { derivePrivateKeyBIP44 } from 'xno-skills';
const privateKey = derivePrivateKeyBIP44(mnemonic, 0);
// Returns: "0123456789abcdef..." (64 hex characters)derivePublicKeyBIP44(privateKey: string): string
Derive a public key from a BIP44-derived private key.
import { derivePublicKeyBIP44 } from 'xno-skills';
const publicKey = derivePublicKeyBIP44(privateKey);
// Returns: "0123456789abcdef..." (64 hex characters)publicKeyToAddressBIP44(publicKey: string): string
Convert a BIP44-derived public key to a Nano address.
import { publicKeyToAddressBIP44 } from 'xno-skills';
const address = publicKeyToAddressBIP44(publicKey);
// Returns: "nano_1abc123..."validateMnemonicBIP44(mnemonic: string): boolean
Validate a BIP39 mnemonic phrase for BIP44 usage.
import { validateMnemonicBIP44 } from 'xno-skills';
const isValid = validateMnemonicBIP44(mnemonic);
// Returns: true or falsemnemonicToBIP39Seed(mnemonic: string, passphrase?: string): string
Convert a mnemonic to a BIP39 seed (512-bit).
import { mnemonicToBIP39Seed } from 'xno-skills';
const seed = mnemonicToBIP39Seed(mnemonic);
// Returns: "0123456789abcdef..." (128 hex characters)Address Validation
validateAddress(address: string): ValidateAddressResult
Validate a Nano address and extract the public key.
import { validateAddress } from 'xno-skills';
const result = validateAddress('nano_1abc123...');
// Returns: { valid: true, publicKey: "..." }
// Or: { valid: false, error: "Invalid prefix..." }Unit Conversion
nanoToRaw(nano: string): string
Convert Nano (XNO) to raw units.
import { nanoToRaw } from 'xno-skills';
const raw = nanoToRaw('1.5');
// Returns: "1500000000000000000000000000000"rawToNano(raw: string, decimals?: number): string
Convert raw units to Nano (XNO).
import { rawToNano } from 'xno-skills';
const nano = rawToNano('1500000000000000000000000000000');
// Returns: "1.5"
// With specific decimal places
const nano = rawToNano(raw, 6);
// Returns: "1.500000"formatNano(raw: string): string
Format raw units as Nano with full precision.
import { formatNano } from 'xno-skills';
const formatted = formatNano('1500000000000000000000000000000');
// Returns: "1.5"knanoToRaw(knano: string): string
Convert kilo-nano to raw units.
import { knanoToRaw } from 'xno-skills';
const raw = knanoToRaw('1.5');
// Returns: "1500000000000000000000000000000000"mnanoToRaw(mnano: string): string
Convert mega-nano to raw units.
import { mnanoToRaw } from 'xno-skills';
const raw = mnanoToRaw('1.5');
// Returns: "1500000000000000000000000000000000000"Cryptographic Functions
blake2b256(data: Uint8Array): Uint8Array
Compute BLAKE2b-256 hash (32 bytes).
import { blake2b256 } from 'xno-skills';
const hash = blake2b256(new TextEncoder().encode('hello'));
// Returns: Uint8Array(32)blake2b512(data: Uint8Array): Uint8Array
Compute BLAKE2b-512 hash (64 bytes).
import { blake2b512 } from 'xno-skills';
const hash = blake2b512(new TextEncoder().encode('hello'));
// Returns: Uint8Array(64)blake2b256Hex(data: Uint8Array): string
Compute BLAKE2b-256 hash and return as hex string.
import { blake2b256Hex } from 'xno-skills';
const hash = blake2b256Hex(new TextEncoder().encode('hello'));
// Returns: "abc123..." (64 hex characters)Base32 Encoding
base32Encode(bytes: Uint8Array): string
Encode bytes to Nano's Base32 format.
import { base32Encode } from 'xno-skills';
const encoded = base32Encode(new Uint8Array([0x00, 0xff]));
// Returns: "1z"base32Decode(str: string): Uint8Array
Decode Nano's Base32 format to bytes.
import { base32Decode } from 'xno-skills';
const bytes = base32Decode('1z');
// Returns: Uint8Array([0x00, 0xff])QR Code Generation
generateAsciiQr(address: string, amount?: number): Promise<string>
Generate an ASCII QR code for a Nano address.
import { generateAsciiQr } from 'xno-skills';
const qr = await generateAsciiQr('nano_1abc123...');
console.log(qr);
// With amount
const qr = await generateAsciiQr('nano_1abc123...', 1.5);Security Notes
CRITICAL: Handle seeds and private keys with extreme care.
Never share your seed or private keys. Anyone with access to these can fully control your wallet.
Store seeds securely. Use hardware wallets, encrypted storage, or offline backup methods. Never store seeds in plain text files, cloud storage, or version control.
Use environment variables for seeds in development. Never hardcode seeds in your source code.
BIP44 vs Legacy derivation. This library supports both:
- Legacy: Uses Blake2b-based path derivation (original Nano method)
- BIP44: Standard HD wallet derivation path
m/44'/165'/[index]'
Choose the method compatible with your existing wallet. Most Nano wallets use legacy derivation.
Mnemonic phrases. When using BIP44, the mnemonic phrase is the master key. Protect it as carefully as a seed.
Passphrase protection. BIP44 supports optional passphrases for additional security. A passphrase acts as a "25th word" - even if someone obtains your mnemonic, they cannot access funds without the passphrase.
Address validation. Always validate addresses before sending funds. Nano addresses include a checksum to catch typos.
Unit precision. Nano uses 30 decimal places. Always use string-based conversion functions to avoid floating-point precision errors.
Development
# Install dependencies
npm install
# Run unit tests
npm test
# Run integration tests
npx vitest run test/integration.test.ts
# Build
npm run build
# Build ESM
npm run build:esm
# Build CJS
npm run build:cjsReleasing
See RELEASING.md.
Similar Projects
- kilkelly/nano-currency-mcp-server — MCP server for Nano with a simple per-transaction send limit
- strawberry-labs/berrypay-cli — Nano wallet CLI for AI agents with payment processing and auto-sweep
License
MIT
