* Runs in all web browsers and mobile frameworks built with Javascript
* Convert Nano units
* Sign any strings with the private key, for example using a password for the user created from the user ID.
+* Validate addresses and mnemonic words
---
```javascript
import { tools } from 'nanocurrency-web'
-const privateKey = '781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3';
+const privateKey = '781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3'
const signed = tools.sign(privateKey, 'foo@bar.com')
```
+#### Validating values
+
+```javascript
+import { tools } from 'nanocurrency-web'
+
+// Validate Nano address
+const valid = tools.validateAddress('nano_1pu7p5n3ghq1i1p4rhmek41f5add1uh34xpb94nkbxe8g4a6x1p69emk8y1d')
+
+// Validate mnemonic words
+const valid = tools.validateMnemonic('edge defense waste choose enrich upon flee junk siren film clown finish luggage leader kid quick brick print evidence swap drill paddle truly occur')
+```
+
### In web
```html
-<script src="https://unpkg.com/nanocurrency-web@1.2.0" type="text/javascript"></script>
+<script src="https://unpkg.com/nanocurrency-web@1.2.1" type="text/javascript"></script>
<script type="text/javascript">
NanocurrencyWeb.wallet.generate(...);
</script>
+import BigNumber from 'bignumber.js'
+
import AddressGenerator from './lib/address-generator'
import AddressImporter, { Account, Wallet } from './lib/address-importer'
-import BlockSigner, { SendBlock, ReceiveBlock, RepresentativeBlock, SignedBlock } from './lib/block-signer'
-import BigNumber from 'bignumber.js'
+import BlockSigner, { ReceiveBlock, RepresentativeBlock, SendBlock, SignedBlock } from './lib/block-signer'
+import NanoAddress from './lib/nano-address'
import NanoConverter from './lib/nano-converter'
import Signer from './lib/signer'
import Convert from './lib/util/convert'
+const nanoAddress = new NanoAddress()
const generator = new AddressGenerator()
const importer = new AddressImporter()
-const signer = new Signer();
+const signer = new Signer()
+
const wallet = {
/**
/**
* Import Nano cryptocurrency accounts from a legacy hex seed
- *
+ *
* This function imports a wallet from a seed. The private key is derived from the seed using
* simply a blake2b hash function. The public key is derived from the private key using the ed25519 curve
* algorithm.
- *
+ *
* The Nano address is derived from the public key using standard Nano encoding.
* The address is prefixed with 'nano_'.
- *
+ *
* @param {string} seed - The seed
* @returns the wallet derived from the seed (seed, account)
- *
+ *
*/
fromLegacySeed: (seed: string): Wallet => {
return importer.fromLegacySeed(seed);
/**
* Sign any strings with the user's private key
- *
+ *
* @param {string} privateKey The private key to sign with
* @param {...string} input Data to sign
*/
return signer.sign(privateKey, ...data);
},
+ /**
+ * Validate a Nano address
+ *
+ * @param {string} input The address to validate
+ */
+ validateAddress: (input: string): boolean => {
+ return nanoAddress.validateNanoAddress(input);
+ },
+
+ /**
+ * Validate mnemonic words
+ *
+ * @param {string} input The address to validate
+ */
+ validateMnemonic: (input: string): boolean => {
+ return importer.validateMnemonic(input);
+ },
+
}
export {
/**
* Import a wallet using a mnemonic phrase
- *
- * @param {string} mnemonic - The mnemonic words to import the wallet from
+ *
+ * @param {string} mnemonic - The mnemonic words to import the wallet from
* @param {string} [seedPassword] - (Optional) The password to use to secure the mnemonic
* @returns {Wallet} - The wallet derived from the mnemonic phrase
*/
return this.nano(seed, 0, 0, mnemonic)
}
+ /**
+ * Validate mnemonic words
+ *
+ * @param mnemonic {string} mnemonic - The mnemonic words to validate
+ */
+ validateMnemonic(mnemonic: string): boolean {
+ const bip39 = new Bip39Mnemonic()
+ return bip39.validateMnemonic(mnemonic);
+ }
+
/**
* Import a wallet using a seed
- *
- * @param {string} seed - The seed to import the wallet from
+ *
+ * @param {string} seed - The seed to import the wallet from
* @param {number} [from] - (Optional) The start index of the private keys to derive from
* @param {number} [to] - (Optional) The end index of the private keys to derive to
* @returns {Wallet} The wallet derived from the mnemonic phrase
return this.nano(seed, from, to, undefined)
}
-
+
/**
* Import a wallet using a legacy seed
- *
+ *
* @param {string} seed - The seed to import the wallet from
* @param {number} [from] - (Optional) The start index of the private keys to derive from
* @param {number} [to] - (Optional) The end index of the private keys to derive to
/**
* Derives the private keys
- *
+ *
* @param {string} seed - The seed to use for private key derivation
* @param {number} from - The start index of private keys to derive from
* @param {number} to - The end index of private keys to derive to
+//@ts-ignore
+import { PBKDF2, SHA256, algo, enc, lib } from 'crypto-js'
+
import Convert from './util/convert'
import Util from './util/util'
import words from './words'
-//@ts-ignore
-import { algo, enc, lib, PBKDF2, SHA256 } from 'crypto-js'
-
export default class Bip39Mnemonic {
password: string
- constructor(password: string) {
+ constructor(password?: string) {
this.password = password
}
/**
* Creates a new wallet
- *
+ *
* @param {string} [entropy] - (Optional) the entropy to use instead of generating
* @returns {MnemonicSeed} The mnemonic phrase and a seed derived from the (generated) entropy
*/
/**
* Validates a mnemonic phrase
- *
+ *
* @param {string} mnemonic - The mnemonic phrase to validate
* @returns {boolean} Is the mnemonic phrase valid
*/
const newChecksum = this.calculateChecksum(entropyHex)
const inputChecksum = Convert.binaryToHexString(checksumBits)
- if (newChecksum != inputChecksum) {
+ if (parseInt(newChecksum, 16) != parseInt(inputChecksum, 16)) {
return false
}
-import Convert from './util/convert'
-
//@ts-ignore
import { blake2b } from 'blakejs'
+import Convert from './util/convert'
+
export default class NanoAddress {
readonly alphabet = '13456789abcdefghijkmnopqrstuwxyz'
* @param {string} address Nano address
*/
validateNanoAddress = (address: string): boolean => {
- /** Ensure the address is provided */
if (address === undefined) {
throw Error('Address must be defined.')
}
- /** Ensure the address is a string */
if (typeof address !== 'string') {
throw TypeError('Address must be a string.')
}
- /** The array of allowed prefixes */
const allowedPrefixes: string[] = ['nano', 'xrb']
-
- /** The regex pattern for validating the address */
const pattern = new RegExp(
`^(${allowedPrefixes.join('|')})_[13]{1}[13456789abcdefghijkmnopqrstuwxyz]{59}$`,
)
- /** Validate the syntax of the address */
- if (!pattern.test(address)) return false
+ if (!pattern.test(address)) {
+ return false
+ }
- /** The expected checksum as a base32-encoded string */
const expectedChecksum = address.slice(-8)
-
- /** The public key as a base32-encoded string */
const publicKey = address.slice(address.indexOf('_') + 1, -8)
-
- /** The public key as an array buffer */
const publicKeyBuffer = this.decodeNanoBase32(publicKey)
-
- /** The actual checksum as an array buffer */
const actualChecksumBuffer = blake2b(publicKeyBuffer, null, 5).reverse()
-
- /** The actual checksum as a base32-encoded string */
const actualChecksum = this.encodeNanoBase32(actualChecksumBuffer)
- /** Validate the provided checksum against the derived checksum */
return expectedChecksum === actualChecksum
}
{
"name": "nanocurrency-web",
- "version": "1.2.0",
+ "version": "1.2.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
{
"name": "nanocurrency-web",
- "version": "1.2.0",
+ "version": "1.2.1",
"description": "Toolkit for Nano cryptocurrency client side offline integrations",
"author": "Miro Metsänheimo <miro@metsanheimo.fi>",
"license": "MIT",
})
// Test vectors from https://docs.nano.org/integration-guides/key-management/
-describe('import wallet with official test vectors test', () => {
+describe('import wallet with test vectors test', () => {
it('should successfully import a wallet with the official Nano test vectors mnemonic', () => {
const result = wallet.fromMnemonic(
expect(result.accounts[0].address).to.equal('nano_1pu7p5n3ghq1i1p4rhmek41f5add1uh34xpb94nkbxe8g4a6x1p69emk8y1d')
})
+ it('should successfully import a wallet with the checksum starting with a zero', () => {
+ wallet.fromMnemonic('food define cancel major spoon trash cigar basic aim bless wolf win ability seek paddle bench seed century group they mercy address monkey cake')
+ })
+
it('should successfully import a wallet with the official Nano test vectors seed', () => {
const result = wallet.fromSeed('0dc285fde768f7ff29b66ce7252d56ed92fe003b605907f7a4f683c3dc8586d34a914d3c71fc099bb38ee4a59e5b081a3497b7a323e90cc68f67b5837690310c')
expect(result).to.have.own.property('mnemonic')