Zero dependency Nano currency wallet for Browser, Node.js & CLI
NPM:
npm install @nano/walletBrowser (CDN):
<script src="https://unpkg.com/@nano/wallet"></script>CLI (global):
npm install -g @nano/wallet// CommonJS (require)
const nano = require('@nano/wallet')
// ESM (import)
import nano from '@nano/wallet'
// ESM named imports
import { generate, convert, send, receive, change_rep } from '@nano/wallet'
// Browser (global)
// <script src="https://unpkg.com/@nano/wallet"></script>
// Available as window.nanoconst nano = require('@nano/wallet')
nano.app({
node: 'https://rpc.nano.to',
rpc_key: 'YOUR_API_KEY', // free key @ rpc.nano.to
database: 'encrypted_wallet.txt',
secret: 'SUPER_SECRET_PASSWORD'
})
;(async () => {
// Create checkout
var payment = await nano.checkout({ amount: '0.00133' })
console.log(payment.browser)
// Wait for payment
var success = await nano.waitFor(payment)
// Receive pending
var receive = await nano.receive()
// Send payment
var send = await nano.send({
to: 'YOUR_FRIENDS_ADDRESS',
amount: '0.00133'
})
console.log(send)
})()This wallet works seamlessly with rpc.nano.to, a free and paid RPC-as-a-Service for the Nano network.
// Free RPC
nano.app({ node: 'https://rpc.nano.to' })
// With API key (higher limits)
nano.app({
node: 'https://rpc.nano.to',
rpc_key: 'YOUR_API_KEY'
})
// Raw RPC calls
await nano.rpc({ action: "block_count" })
// { "count": "215474654", "unchecked": "4", "cemented": "215474654" }
await nano.rpc({ action: "account_info", account: "nano_1abc..." })
await nano.rpc({ action: "process", json_block: "true", subtype: "send", block: signedBlock })npm install -g @nano/walletQuick Start — 5 commands to send your first Nano:
# 1. Create a wallet (encrypted locally)
nano-wallet generate --secret mypassword
# Wallet saved to ./nano-wallet.dat
# Address: nano_3abc...
# Mnemonic: word1 word2 word3 ...
# 2. Print your address (easy copy/paste + browser receive link)
nano-wallet address --secret mypassword
# Address: nano_3abc...
# Receive: https://nano.to/nano_3abc...
# 3. Claim free Nano from the built-in faucet
nano-wallet faucet --secret mypassword
# Faucet claimed! 0.001 NANO is on the way.
# hash: ABC123...
# to: nano_3abc...
# 4. Receive the pending Nano
nano-wallet receive --secret mypassword
# Received 1 block(s):
# 0.001 NANO — hash: ABC123...
# 5. Check your balance
nano-wallet balance --secret mypassword
# 6. Change representative (auto-picks a good one)
nano-wallet change_rep --secret mypassword
# or specify one:
# nano-wallet change_rep nano_1anr... --secret mypassword
# 7. Send Nano to anyone
nano-wallet send nano_1to... 0.00005 --secret mypassword
# Sent 0.00005 NANO
# to: nano_1to...
# hash: DEF456...
# view: https://nanobrowse.com/block/DEF456...With environment variables (even cleaner):
export NANO_SECRET=mypassword
nano-wallet generate
nano-wallet receive
nano-wallet send nano_1to... 0.001
nano-wallet balanceUtility commands (no wallet needed):
# Convert units
nano-wallet convert 1.5 NANO RAW
nano-wallet convert 1000000000000000000000000000000 RAW NANO
# Raw RPC calls
nano-wallet rpc block_count
nano-wallet rpc account_info account=nano_1abc...
# Check any address (no wallet required)
nano-wallet balance nano_1abc...
nano-wallet account_info nano_1abc...
# Print your address and nano.to receive link
nano-wallet address --secret mypassword
# Claim free test Nano (requires NANO_RPC_KEY)
nano-wallet faucet --secret mypassword
nano-wallet faucet nano_3abc... --secret mypassword # or pass address directly
# Encrypt / decrypt files
nano-wallet encrypt myfile.json mypassword
nano-wallet decrypt encrypted.txt mypassword
# Sign a block manually
nano-wallet sign '{"walletBalanceRaw":"1000...","toAddress":"nano_1..."}' PRIVATE_KEYAll options:
Options:
--secret <password> Wallet password (encrypts/decrypts wallet file)
--wallet <file> Wallet file path (default: ./nano-wallet.dat)
--node <url> RPC endpoint (default: https://rpc.nano.to)
--key <api_key> RPC API key for rpc.nano.to
--json Output raw JSON
Environment Variables:
NANO_SECRET Wallet password
NANO_WALLET Wallet file path
NANO_RPC RPC endpoint
NANO_RPC_KEY API key for rpc.nano.to
nano.generate()nano.import( nano.generate() )nano.accounts()nano.add_account()nano.sign(block)nano.convert('421.70', 'NANO', 'RAW') // 421700000000000000000000000000000nano.encrypt('any_string', process.env.PASSWORD) // AES-256nano.decrypt('any_string', process.env.PASSWORD) // UTF-8nano.export()Build non-custodial Nano applications with AES-256 encrypted wallet persistence.
nano.app({
node: 'https://rpc.nano.to',
rpc_key: 'RPC_API_KEY', // get free key @ rpc.nano.to
database: 'aes_string.txt',
secret: 'SUPER_SECRET_PASSWORD'
})
console.log( nano.accounts() )
await nano.receive()
await nano.send({ to: '@faucet', amount: 0.001 })nano.app({
node: 'https://rpc.nano.to',
rpc_key: 'RPC_API_KEY',
database: 'aes_string.txt',
secret: 'SUPER_SECRET_PASSWORD'
})
const user = { userId: 'JoeDoe' }
console.log( nano.add_account(user) )
await nano.receive(user)
var balance = await nano.balance(user)
console.log( balance )
await nano.send({
to: user,
from: 0,
amount: '0.0000133'
})// get all balances
await nano.balances()
// get balance of specific address
await nano.balance({ userId: 'johnDoe' })
// {
// "balance": "325586539664609129644855132177",
// "pending": "2309372510769300000000000000000000",
// "receivable": "2309372510769300000000000000000000",
// "balance_nano": "0.32558653966460912964",
// "pending_nano": "2309.3725107693",
// "receivable_nano": "2309.3725107693"
// }// send to globally known accounts
await nano.send({ to: '@fosse', amount: 0.1 })
// send to multiple accounts
await nano.send({ to: [ '@fosse', '@bank' ], amount: 0.1 })
// send all funds on address
await nano.send({ to: '@fosse', amount: 'all' })
// transfer between your own accounts
await nano.send({ to: 1, from: 0, amount: 0.1 })
// transfer between your own users
await nano.send({ to: { userId: 'johnDoe' }, from: { userId: 'janeDoe' }, amount: 0.1 })// receive all
await nano.receive()
// receive all for specific address
await nano.receive({ userId: 'johnDoe' })// auto-pick a good rep from rpc.nano.to
await nano.change_rep()
// specify a rep address
await nano.change_rep('nano_1anrzcuwe64rwxzcco8dkhpyxpi8kd7zsjc1oeimpc3ppca4mrjtwnqposrs')
// with config object
await nano.change_rep({ rep: 'nano_1anrzcuwe64rwxzcco8dkhpyxpi8kd7zsjc1oeimpc3ppca4mrjtwnqposrs' })
// {
// "hash": "BLOCK_HASH",
// "representative": "nano_1anr...",
// "account": "nano_3abc...",
// "browser": "https://nanobrowse.com/block/BLOCK_HASH"
// }When no representative address is provided, the wallet fetches a list of available representatives from rpc.nano.to (via the reps RPC action) and automatically picks one at random.
var checkout = await nano.checkout({ address: 0, amount: '0.133' })
console.log( checkout )
// {
// "id": "CHECKOUT_ID",
// "browser": "https://nano.to/id_CHECKOUT_ID",
// "json": "https://api.nano.to/checkout/CHECKOUT_ID",
// "check": "https://api.nano.to/check/CHECKOUT_ID",
// "address": "YOUR_ADDRESS",
// "qrcode": "data:image/png;base64"
// }
var payment = await nano.waitFor(checkout)
console.log( payment )
// {
// id: 'b06a8127',
// success: true,
// block: '3C0D9A50649C6BE...',
// amount: '0.133',
// amount_raw: '1330000000000000000000000'
// }SEND
var send = nano.sign({
walletBalanceRaw: '18618869000000000000000000000000',
toAddress: 'nano_3kyb49tqpt39ekc49kbej51ecsjqnimnzw1swxz4boix4ctm93w517umuiw8',
representativeAddress: 'nano_1stofnrxuz3cai7ze75o174bpm7scwj9jn3nxsn8ntzg784jf1gzn1jjdkou',
frontier: '92BA74A7D6DC7557F3EDA95ADC6341D51AC777A0A6FF0688A5C492AB2B2CB40D',
transactionHash: 'CBC911F57B6827649423C92C88C0C56637A4274FF019E77E24D61D12B5338783',
amountRaw: '7000000000000000000000000000000',
}, process.env.PRIVATE_KEY) RECEIVE
var receive = nano.sign({
walletBalanceRaw: '18618869000000000000000000000000',
toAddress: 'nano_3kyb49tqpt39ekc49kbej51ecsjqnimnzw1swxz4boix4ctm93w517umuiw8',
representativeAddress: 'nano_1stofnrxuz3cai7ze75o174bpm7scwj9jn3nxsn8ntzg784jf1gzn1jjdkou',
frontier: '92BA74A7D6DC7557F3EDA95ADC6341D51AC777A0A6FF0688A5C492AB2B2CB40D',
transactionHash: 'CBC911F57B6827649423C92C88C0C56637A4274FF019E77E24D61D12B5338783',
amountRaw: '7000000000000000000000000000000',
work: 'c5cf86de24b24419',
}, process.env.PRIVATE_KEY)
var hash = await nano.process( receive )CHANGE_REP
var change_rep = nano.sign({
walletBalanceRaw: '3000000000000000000000000000000',
address: 'nano_3igf8hd4sjshoibbbkeitmgkp1o6ug4xads43j6e4gqkj5xk5o83j8ja9php',
representativeAddress: 'nano_1anrzcuwe64rwxzcco8dkhpyxpi8kd7zsjc1oeimpc3ppca4mrjtwnqposrs',
frontier: '128106287002E595F479ACD615C818117FCB3860EC112670557A2467386249D4',
work: '0000000000000000',
}, process.env.PRIVATE_KEY)
var hash = await nano.process( change_rep )JSON object, stringified and encrypted with AES-256
nano.offline({
filename: 'aes_string.txt',
password: process.env.PASSWORD
})
console.log( nano.export() )This package has zero external npm dependencies. All cryptographic libraries are vendored and sandboxed directly in the source:
- nanocurrency-web-js — Wallet generation, block signing, Ed25519, Blake2b
- crypto-js — AES-256 encryption (unified across Browser & Node.js)
This eliminates supply chain attack vectors while keeping the package fully self-contained.
v3 replaced the aes256 npm dependency with the vendored CryptoJS library for encryption. Existing v1.x wallets are automatically migrated when loaded — no action is required in most cases.
When nano.offline() or nano.import() detects a legacy wallet, it:
- Decrypts with the old format (AES-256-CTR)
- Re-encrypts with the new format (AES-256-CBC)
- Saves the updated file
// Just load your wallet as usual — migration happens automatically
nano.offline({ database: 'my_old_wallet.txt', secret: 'my_password' })
// Console: @nano/wallet: Migrated wallet from legacy format to AES-256-CBC.// Migrate a wallet file without loading it
nano.migrate({ database: 'my_old_wallet.txt', secret: 'my_password' })
// { migrated: true, accounts: 1, file: 'my_old_wallet.txt' }# Decrypt legacy wallet and inspect
nano-wallet decrypt my_old_wallet.txt my_password
# Re-encrypt in new format
nano-wallet encrypt decrypted_output.json my_password > my_new_wallet.txtNote: Browser wallets are unaffected — they always used CryptoJS.
Claim 0.001 free test NANO in one step. Requires an RPC key from rpc.nano.to.
nano.app({
node: 'https://rpc.nano.to',
rpc_key: 'YOUR_RPC_KEY',
database: 'nano-wallet.dat',
secret: 'mypassword'
})
// Auto-detect address from loaded wallet
var result = await nano.faucet()
// Or pass an address explicitly
var result = await nano.faucet('nano_3abc...')
console.log(result)
// {
// success: true,
// message: '0.001 NANO sent to nano_3abc...',
// claim: {
// id: 42,
// nano_address: 'nano_3abc...',
// amount: '0.001',
// status: 'sent',
// tx_hash: 'ABC123...',
// created_at: '2026-02-18T12:00:00.000Z'
// }
// }- Built-in faucet:
nano-wallet faucet --secret mypassword(requires RPC key) - https://nanodrop.io/
- https://freenanofaucet.com/
- https://getnano.ovh/faucet
MIT

