Build a wallet with navio-sdk¶
Goal: a Node.js CLI that creates a wallet, syncs to the chain, shows balance, and sends NAV. ~100 lines of code.
Prereqs¶
naviodrunning locally on testnet, or an Electrum server you trust.- Node.js ≥ 20.19.
- A fresh project directory.
mkdir navio-wallet-cli && cd navio-wallet-cli
npm init -y
npm install navio-sdk
npm install -D typescript tsx @types/node
npx tsc --init --target ES2022 --module nodenext --moduleResolution nodenext --esModuleInterop --strict
The CLI¶
src/index.ts:
import { NavioClient } from 'navio-sdk';
import { argv, exit } from 'node:process';
const DB_PATH = './wallet.db';
const ELECTRUM = { host: 'localhost', port: 50005, ssl: false };
async function open(createIfMissing = false) {
const client = new NavioClient({
walletDbPath: DB_PATH,
electrum: ELECTRUM,
network: 'testnet',
createWalletIfNotExists: createIfMissing,
});
await client.initialize();
return client;
}
async function cmdCreate() {
const client = await open(true);
const km = client.getKeyManager();
console.log('MNEMONIC (write this down):');
console.log(km.getMnemonic());
console.log();
const addr = km.getSubAddressBech32m({ account: 0, address: 0 }, 'testnet');
console.log('Receive address:', addr);
await client.disconnect();
}
async function cmdSync() {
const client = await open();
await client.sync({
onProgress: (h, tip) => {
process.stdout.write(`\rsync ${h}/${tip} (${((h / tip) * 100).toFixed(1)}%) `);
},
});
process.stdout.write('\n');
await client.disconnect();
}
async function cmdBalance() {
const client = await open();
const nav = await client.getBalanceNav();
const utxos = await client.getUnspentOutputs();
console.log(`${nav.toFixed(8)} NAV in ${utxos.length} UTXOs`);
await client.disconnect();
}
async function cmdSend(address: string, amountNav: string) {
const client = await open();
const sats = BigInt(Math.round(parseFloat(amountNav) * 1e8));
const res = await client.sendTransaction({ address, amount: sats });
console.log('sent:', res.txId);
console.log('fee: ', Number(res.fee) / 1e8, 'NAV');
await client.disconnect();
}
const [, , cmd, ...rest] = argv;
try {
switch (cmd) {
case 'create': await cmdCreate(); break;
case 'sync': await cmdSync(); break;
case 'balance': await cmdBalance(); break;
case 'send': await cmdSend(rest[0], rest[1]); break;
default:
console.log('usage: wallet <create|sync|balance|send <addr> <amount>>');
exit(1);
}
} catch (e) {
console.error((e as Error).message);
exit(1);
}
package.json scripts:
Use it¶
npm run wallet create
# MNEMONIC: abandon ability ... (write down!)
# Receive address: tnv1...
# Get some testnet NAV via /faucet in Discord, then:
npm run wallet sync
# sync 123456/123500 (100.0%)
npm run wallet balance
# 10.00000000 NAV in 1 UTXOs
npm run wallet send tnv1...recipient... 1
# sent: <txid>
# fee: 0.00010000 NAV
Extensions¶
- Add
restorecommand accepting a mnemonic — passrestoreFromMnemonic+restoreFromHeightto the config. - Add
watchcommand that runsstartBackgroundSyncand prints new-block / new-tx events. - Add
encrypt+unlock— callkm.setPassword(pw)/km.unlock(pw). - Add
tokenslisting asset balances viaclient.getAssetBalances(). - Replace Electrum with direct P2P — switch the config.
All APIs used here are documented in the SDK section.