npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

libnemo

v0.0.21

Published

Asynchronous, non-blocking Nano cryptocurrency integration toolkit.

Downloads

57

Readme

libnemo

libnemo is a fork of the nanocurrency-web toolkit. It is used for client-side implementations of Nano cryptocurrency wallets and enables building web-based applications that can work even while offline. libnemo supports managing wallets, deriving accounts, signing blocks, and more.

It utilizes the Web Crypto API which is native to all modern browsers; as such, it has only a required dependency in order to work with the BLAKE2b algorithm. Optionally, Ledger device dependencies can be installed to enable Ledger hardware wallet support.

Features

  • Generate new BIP-32 hierarchial deterministic (HD) wallets with a BIP-39 mnemonic phrase and the Nano path registered with BIP-44. Used by Ledger hardware wallet.
  • Generate new BLAKE2b wallets with a BIP-39 mnemonic phrases. Original method described by nano spec.
  • Import wallets with a mnemonic phrase or a seed.
  • Derive indexed accounts with a Nano address and a public-private keypair.
  • Create, sign, and verify send, receive, and change blocks.
  • Get account info and process blocks on the network while online.
  • Manage known addresses with a rolodex.
  • Sign and verify arbitrary strings with relevant keys.
  • Validate entropy, seeds, mnemonic phrases, and Nano addresses.
  • Convert Nano unit denominations.
  • Run in modern web browsers and mobile frameworks built with Javascript without server-side NodeJS functions.

Installation

From NPM

npm install libnemo

Usage

⚠️ The examples below should never be used for real transactions! ⚠️

Wallets and accounts

At its core, a wallet is a hexadecimal string called a seed. From this seed, millions of unique accounts can be deterministically derived. The first account in a wallet starts at index 0.

For clarity, the following terms are used throughout the library:

  • BIP-32 - Defines how hierarchical determinstic (HD) wallets are generated
  • BIP-39 - Defines how mnemonic phrases are generated
  • BIP-44 - Expands on BIP-32 to define how an enhanced derivation path can allow a single wallet to store multiple currencies

libnemo is able to generate and import HD and BLAKE2b wallets, and it can derive accounts for both. An HD wallet seed is 128 characters while a BLAKE2b wallet seed is 64 characters. For enhanced security, libnemo requires a password to create or import wallets, and wallets are initialized in a locked state. More advanced implementations can provide their own CryptoKey instead of a password. Refer to the documentation on each class factory method for specific usage.

import { Bip44Wallet, Blake2bWallet } from 'libnemo'

const wallet = await Bip44Wallet.create(password)
const wallet = await Bip44Wallet.fromEntropy(password, entropy, salt?)
const wallet = await Bip44Wallet.fromMnemonic(password, mnemonic, salt?)
const wallet = await Bip44Wallet.fromSeed(password, seed)

const wallet = await Bip44Wallet.create(password)
const wallet = await Bip44Wallet.fromSeed(password, seed)
const wallet = await Bip44Wallet.fromMnemonic(password, mnemonic)
try {
	const unlockResult = await wallet.unlock(password)
} catch(err) {
	console.log(err)
}
console.log(unlockResult) // true if successfully unlocked

const { mnemonic, seed } = wallet

const accounts = await wallet.accounts(from?, to?)
const firstAccount = accounts[0]
const { address, publicKey, privateKey, index } = firstAccount

const nodeUrl = 'https://nano-node.example.com/'
await firstAccount.refresh(nodeUrl) // online
const { frontier, balance, representative } = firstAccount

Blocks

Blocks do not contain transaction amounts. Instead, they contain stateful balance changes only. For example, if sending Ӿ5 from an account with a balance of Ӿ20, the send block would contain balance: Ӿ15 (psuedocode for demonstration purposes and not a literal depiction). This can be difficult to track, so libnemo provides the convenience of specifying an amount to send or receive and calculates the balance change itself.

All blocks are 'state' types, but they are interpreted as one of three different subtypes based on the data they contain: send, receive, or change representative. libnemo implements them as the following classes:

  • SendBlock: the Nano balance of the account decreases
  • ReceivBlock: the Nano balance of the account increases and requires a matching SendBlock
  • ChangeBlock: the representative for the account changes while the Nano balance does not

Nano protocol allows changing the representative at the same time as a balance change. libnemo does not implement this for purposes of clarity; all ChangeBlock objects will maintain the same Nano balance.

Always fetch the most up to date information for the account from the network using the account_info RPC command which can then be used to populate the block parameters.

Blocks require a small proof-of-work that must be calculated for the block to be accepted by the network. This can be provided when creating the block, or a public node that allows the work_generate RPC command can be used.

Finally, the block must be signed with the private key of the account. libnemo enables this to be done offline if desired. After being signed, the block can be published to the network with the process RPC command.

Creating blocks

import { SendBlock, ReceiveBlock, ChangeBlock } from 'libnemo'

const send = new SendBlock(
	'nano_1pu7p5n3ghq1i1p4rhmek41f5add1uh34xpb94nkbxe8g4a6x1p69emk8y1d', // sender
	'5618869000000000000000000000000',                                   // current balance
	'nano_3phqgrqbso99xojkb1bijmfryo7dy1k38ep1o3k3yrhb7rqu1h1k47yu78gz', // recipient
	'2000000000000000000000000000000',                                   // amount to send
	'nano_1stofnrxuz3cai7ze75o174bpm7scwj9jn3nxsn8ntzg784jf1gzn1jjdkou', // representative
	'92BA74A7D6DC7557F3EDA95ADC6341D51AC777A0A6FF0688A5C492AB2B2CB40D',  // hash of previous block
	'fbffed7c73b61367'                                                   // PoW nonce (optional at first but required to process)
)

const receive = new ReceiveBlock(
	'nano_1pu7p5n3ghq1i1p4rhmek41f5add1uh34xpb94nkbxe8g4a6x1p69emk8y1d', // recipient
	'18618869000000000000000000000000',                                  // current balance
	'CBC911F57B6827649423C92C88C0C56637A4274FF019E77E24D61D12B5338783',  // origin (hash of matching send block)
	'7000000000000000000000000000000',                                   // amount that was sent
	'nano_1stofnrxuz3cai7ze75o174bpm7scwj9jn3nxsn8ntzg784jf1gzn1jjdkou', // representative
	'92BA74A7D6DC7557F3EDA95ADC6341D51AC777A0A6FF0688A5C492AB2B2CB40D',  // hash of previous block
	'c5cf86de24b24419'                                                   // PoW nonce (optional at first but required to process)
)

const change = new ChangeBlock(
	'nano_1pu7p5n3ghq1i1p4rhmek41f5add1uh34xpb94nkbxe8g4a6x1p69emk8y1d', // account redelegating vote weight
	'3000000000000000000000000000000',                                   // current balance
	'nano_1anrzcuwe64rwxzcco8dkhpyxpi8kd7zsjc1oeimpc3ppca4mrjtwnqposrs', // new representative
	'128106287002E595F479ACD615C818117FCB3860EC112670557A2467386249D4',  // hash of previous block
	'0000000000000000'                                                   // PoW nonce (optional at first but required to process)
)

Signing a block

const privateKey = '3BE4FC2EF3F3B7374E6FC4FB6E7BB153F8A2998B3B3DAB50853EABE128024143'
try {
	await block.sign(privateKey)
} catch (err) {
	console.log(err)
}

Caculating proof-of-work from an online service

const node = new Rpc('https://nano-node.example.com/')
try {
	await block.pow('https://nano-node.example.com/')
} catch (err) {
	console.log(err)
}

Processing a block on the network

const node = new Rpc('https://nano-node.example.com', 'nodes-api-key')
try {
	const hash = await block.process('https://nano-node.example.com/')
} catch (err) {
	console.log(err)
}

Tools

Converting Nano denominations

Raw values are the native unit of exchange throughout libnemo and are represented by the primitive bigint data type. Other supported denominations are as follows: | Unit | Raw | |-------|-----| | RAI | 1024 raw | | NYANO | 1024 raw | | KRAI | 1027 raw | | PICO | 1027 raw | | MRAI | 1030 raw | | NANO | 1030 raw | | KNANO | 1033 raw | | MNANO | 1036 raw |

import { Tools } from 'libnemo'
// Denominations are case-insensitive
const oneNanoToRaw = Tools.convert('1', 'NANO', 'RAW') // 1000000000000000000000000000000
const oneNonillionRawToNano = Tools.convert('1000000000000000000000000000000', 'RAW', 'NANO') // 1
const oneThousandNyanoToPico = Tools.convert('1000', 'nYaNo', 'pico') //1
const oneThousandPicoToNano = Tools.convert('1000', 'pico', 'NANO') // 1

Verifying signatures and signing anything with the private key

Since cryptocurrencies like Nano uses asymmetric keys to sign and verify blocks and transactions, a Nano account itself can be used to sign arbitrary data with its private key and verify signatures from other accounts with their public keys.

For example, a client-side login can be implemented by challenging an account owner to sign their email address using their private key:

import { Tools } from 'libnemo'

const privateKey = '3BE4FC2EF3F3B7374E6FC4FB6E7BB153F8A2998B3B3DAB50853EABE128024143'
const publicKey = '5B65B0E8173EE0802C2C3E6C9080D1A16B06DE1176C938A924F58670904E82C4'
const signature = await Tools.sign(privateKey, '[email protected]')
const isValid = await Tools.verify(publicKey, signature, '[email protected]')

Ownership of a Nano address can also be proven by challenging the account owner to sign an arbitrary string and then validating the signature with the Nano account address.

import { Tools } from 'libnemo'

const address = 'nano_1pu7p5n3ghq1i1p4rhmek41f5add1uh34xpb94nkbxe8g4a6x1p69emk8y1d'
const privateKey = '3BE4FC2EF3F3B7374E6FC4FB6E7BB153F8A2998B3B3DAB50853EABE128024143'
const randomData = new Entropy().hex

const signature = await Tools.sign(privateKey, randomData)
const publicKey = new Account(address).publicKey
const isValid = await Tools.verify(publicKey, signature, randomData)

Validate a Nano account address

import { Tools } from 'libnemo'

const valid = Account.validate('nano_1pu7p5n3ghq1i1p4rhmek41f5add1uh34xpb94nkbxe8g4a6x1p69emk8y1d')

Tests

Test vectors were retrieved from the following publicly-available locations:

  • Nano (BIP-44): https://docs.nano.org/integration-guides/key-management/#test-vectors
  • Trezor (BIP-39): https://github.com/trezor/python-mnemonic/blob/master/vectors.json
  • BIP-32: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#user-content-Test_Vectors Another set of test vectors were created for libnemo based on the Trezor set. These extra test vectors were generated purely to test uncommon yet valid mnemonic phrase lengths like 15 or 18 words.

⚠️ The test vectors should never be used for real transactions! ⚠️

Building

  • npm run build: compile and build
  • npm run test: all of the above, run tests, and print results to the console
  • npm run test:coverage: all of the above, calculate code coverage, and print code coverage to the console
  • npm run test:coverage:report: all of the above, and open an HTML code coverage report in the browser (requires lcov and xdg-open)

Donations

If you find this library helpful, please consider tipping the developer.

nano_1zosoqs47yt47bnfg7sdf46kj7asn58b7uzm9ek95jw7ccatq37898u1zoso