]> zoso.dev Git - libnemo.git/commitdiff
Implemented most features (commit overdue)
authorMiro Metsänheimo <miro@metsanheimo.fi>
Wed, 9 Oct 2019 17:23:07 +0000 (20:23 +0300)
committerMiro Metsänheimo <miro@metsanheimo.fi>
Wed, 9 Oct 2019 17:23:07 +0000 (20:23 +0300)
* Generate random entropy
* BIP39 mnemonic phrase and seed from entropy
* Mnemonic phrase importing and validation
* BIP32 key derivation from seed
* Nano address encoding and validation
* Nano unit converter
* Modified ed25519 curve for Nano
* Block signing for receive and send blocks

16 files changed:
index.ts [new file with mode: 0644]
lib/address-generator.ts [new file with mode: 0644]
lib/address-importer.ts [new file with mode: 0644]
lib/bip32-key-derivation.ts [new file with mode: 0644]
lib/bip39-mnemonic.ts [new file with mode: 0644]
lib/block-signer.ts [new file with mode: 0644]
lib/ed25519.ts [new file with mode: 0644]
lib/nano-address.ts [new file with mode: 0644]
lib/nano-converter.ts [new file with mode: 0644]
lib/util/convert.ts [new file with mode: 0644]
lib/util/curve25519.ts [new file with mode: 0644]
lib/util/util.ts [new file with mode: 0644]
lib/words.ts [new file with mode: 0644]
package-lock.json [new file with mode: 0644]
package.json [new file with mode: 0644]
tsconfig.json [new file with mode: 0644]

diff --git a/index.ts b/index.ts
new file mode 100644 (file)
index 0000000..2fcfdf4
--- /dev/null
+++ b/index.ts
@@ -0,0 +1,117 @@
+import { AddressGenerator } from './lib/address-generator'
+import { AddressImporter } from './lib/address-importer'
+import BlockSigner, { SendBlock, ReceiveBlock } from './lib/block-signer'
+
+const generator = new AddressGenerator()
+const importer = new AddressImporter()
+const wallet = {
+
+       /**
+        * Generate a new Nano cryptocurrency wallet
+        *
+        * This function generates a wallet from random entropy. Wallet includes
+        * a BIP39 mnemonic phrase in line with the Nano Ledger implementation and
+        * a seed, the account is derived using BIP32 deterministic hierarchial algorithm
+        * with input parameters 44'/165' and index 0.
+        *
+        * The Nano address is derived from the public key using standard Nano encoding.
+        * The address is prefixed with 'nano_'.
+        *
+        * Generation uses CryptoJS to generate random entropy. You can give your own entropy
+        * as a parameter and it will be used instead.
+        *
+        * An optional seed password can be used to encrypt the mnemonic phrase so the seed
+        * cannot be derived correctly without the password. Recovering the password is not possible.
+        *
+        * @param {string} [entropy] Optional entropy to be used instead of the default
+        * @param {string} [seedPassword] Optional seed password
+        * @returns the generated mnemonic, seed and account
+        */
+       generate: (entropy?: string, seedPassword?: string) => {
+               return generator.generateWallet(entropy, seedPassword)
+       },
+
+       /**
+        * Import a Nano cryptocurrency wallet from a mnemonic phrase
+        *
+        * This function imports a wallet from a mnemonic phrase. Wallet includes the mnemonic phrase,
+        * a seed derived with BIP39 standard and an account derived using BIP32 deterministic hierarchial
+        * algorithm with input parameters 44'/165' and index 0.
+        *
+        * The Nano address is derived from the public key using standard Nano encoding.
+        * The address is prefixed with 'nano_'.
+        *
+        * @param {string} mnemonic The mnemonic phrase. Words are separated with a space
+        * @param {string} [seedPassword] Optional seed password
+        * @throws Throws an error if the mnemonic phrase doesn't pass validations
+        * @returns the imported mnemonic, seed and account
+        */
+       fromMnemonic: (mnemonic: string, seedPassword?: string) => {
+               return importer.fromMnemonic(mnemonic, seedPassword)
+       },
+
+       /**
+        * Import a Nano cryptocurrency wallet from a seed
+        *
+        * This function imports a wallet from a seed. Wallet includes the seed and an account derived using
+        * BIP39 standard and an account derived using BIP32 deterministic hierarchial algorithm with input
+        * parameters 44'/165' and index 0.
+        *
+        * 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 importes seed and account
+        */
+       fromSeed: (seed: string) => {
+               return importer.fromSeed(seed)
+       },
+
+       /**
+        * Derive accounts for the seed
+        *
+        * This function derives Nano accounts with the BIP32 deterministic hierarchial algorithm
+        * from the given seed with input parameters 44'/165' and indexes based on the from and to
+        * parameters.
+        *
+        * @param {string} seed The seed
+        * @param {number} from The start index
+        * @param {number} to The end index
+        */
+       accounts: (seed: string, from: number, to: number) => {
+               return importer.fromSeed(seed, from, to).accounts
+       },
+
+}
+
+const blockSigner = new BlockSigner()
+const block = {
+
+       /**
+        * Sign a send block with the input parameters
+        *
+        * @param {SendBlock} data The data for the block
+        * @param {string} privateKey Private key to sign the block
+        */
+       send: (data: SendBlock, privateKey: string) => {
+               return blockSigner.send(data, privateKey)
+       },
+
+       /**
+        * Sign a receive block with the input parameters
+        *
+        * @param {SendBlock} data The data for the block
+        * @param {string} privateKey Private key to sign the block
+        */
+       receive: (data: ReceiveBlock, privateKey: string) => {
+               return blockSigner.receive(data, privateKey)
+       },
+
+       // TODO: change representative block
+
+}
+
+export default {
+       wallet,
+       block,
+}
diff --git a/lib/address-generator.ts b/lib/address-generator.ts
new file mode 100644 (file)
index 0000000..b58ed60
--- /dev/null
@@ -0,0 +1,38 @@
+import Bip32KeyDerivation from './bip32-key-derivation'
+import Bip39Mnemonic from './bip39-mnemonic'
+import { Ed25519 } from './ed25519'
+import { NanoAddress } from './nano-address'
+
+export class AddressGenerator {
+
+       /**
+        * Generates the wallet
+        *
+        * @param {String} seedPassword Password for the seed
+        */
+       generateWallet(entropy = '', seedPassword: string = '') {
+               const bip39 = new Bip39Mnemonic(seedPassword)
+               const wallet = bip39.createWallet(entropy)
+
+               const bip44 = new Bip32KeyDerivation(`44'/165'/0'`, wallet.seed)
+               const privateKey = bip44.derivePath().key
+
+               const ed25519 = new Ed25519()
+               const keyPair = ed25519.generateKeys(privateKey)
+
+               const nano = new NanoAddress()
+               const address = nano.deriveAddress(keyPair.publicKey)
+
+               return {
+                       mnemonic: wallet.mnemonic,
+                       seed: wallet.seed,
+                       accounts: [{
+                               accountIndex: 0,
+                               privateKey: keyPair.privateKey,
+                               publicKey: keyPair.publicKey,
+                               address,
+                       }],
+               }
+       }
+
+}
diff --git a/lib/address-importer.ts b/lib/address-importer.ts
new file mode 100644 (file)
index 0000000..5241ce9
--- /dev/null
@@ -0,0 +1,53 @@
+import Bip32KeyDerivation from './bip32-key-derivation'
+import Bip39Mnemonic from './bip39-mnemonic'
+import { Ed25519 } from './ed25519'
+import { NanoAddress } from './nano-address'
+
+export class AddressImporter {
+
+       fromMnemonic(mnemonic: string, seedPassword = '') {
+               const bip39 = new Bip39Mnemonic(seedPassword)
+               if (!bip39.validateMnemonic(mnemonic)) {
+                       throw 'Invalid mnemonic phrase'
+               }
+
+               const seed = bip39.mnemonicToSeed(mnemonic)
+               return this.nano(seed, 0, 0, mnemonic)
+       }
+
+       fromSeed(seed: string, from = 0, to = 0) {
+               return this.nano(seed, from, to, undefined)
+       }
+
+       /**
+        * Generates the wallet
+        * @param {String} seedPassword Password for the seed
+        */
+       private nano(seed: string, from: number, to: number, mnemonic?: string) {
+               const accounts = []
+
+               for (let i = from; i <= to; i++) {
+                       const bip44 = new Bip32KeyDerivation(`44'/165'/${i}'`, seed)
+                       const privateKey = bip44.derivePath().key
+
+                       const ed25519 = new Ed25519()
+                       const keyPair = ed25519.generateKeys(privateKey)
+
+                       const nano = new NanoAddress()
+                       const address = nano.deriveAddress(keyPair.publicKey)
+                       accounts.push({
+                               accountIndex: i,
+                               privateKey: keyPair.privateKey,
+                               publicKey: keyPair.publicKey,
+                               address,
+                       })
+               }
+
+               return {
+                       mnemonic,
+                       seed,
+                       accounts,
+               }
+       }
+
+}
diff --git a/lib/bip32-key-derivation.ts b/lib/bip32-key-derivation.ts
new file mode 100644 (file)
index 0000000..6d22086
--- /dev/null
@@ -0,0 +1,62 @@
+import { Convert } from './util/convert'
+
+const CryptoJS = require('crypto-js')
+
+const ED25519_CURVE = 'ed25519 seed'
+const HARDENED_OFFSET = 0x80000000
+
+export default class Bip32KeyDerivation {
+
+       path: string
+       seed: string
+
+       constructor(path: string, seed: string) {
+               this.path = path
+               this.seed = seed
+       }
+
+       derivePath = () => {
+               const { key, chainCode } = this.getKeyFromSeed()
+               const segments = this.path
+                       .split('/')
+                       .map(v => v.replace('\'', ''))
+                       .map(el => parseInt(el, 10))
+               return segments.reduce(
+                       (parentKeys, segment) =>
+                               this.CKDPriv(parentKeys, segment + HARDENED_OFFSET),
+                       { key, chainCode }
+               )
+       }
+
+       private getKeyFromSeed = () => {
+               return this.derive(
+                       CryptoJS.enc.Hex.parse(this.seed),
+                       CryptoJS.enc.Utf8.parse(ED25519_CURVE))
+       }
+
+       private CKDPriv = ({ key, chainCode }: { key: string, chainCode: string }, index: number) => {
+               const ib = []
+               ib.push((index >> 24) & 0xff)
+               ib.push((index >> 16) & 0xff)
+               ib.push((index >> 8) & 0xff)
+               ib.push(index & 0xff)
+               const data = '00' + key + Convert.ab2hex(new Uint8Array(ib).buffer)
+
+               return this.derive(
+                       CryptoJS.enc.Hex.parse(data),
+                       CryptoJS.enc.Hex.parse(chainCode))
+       }
+
+       private derive = (data: string, base: string) => {
+               const hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA512, base)
+               const I = hmac.update(data).finalize().toString()
+               const IL = I.slice(0, I.length / 2)
+               const IR = I.slice(I.length / 2)
+
+               return {
+                       key: IL,
+                       chainCode: IR,
+               }
+       }
+
+}
diff --git a/lib/bip39-mnemonic.ts b/lib/bip39-mnemonic.ts
new file mode 100644 (file)
index 0000000..8a598ce
--- /dev/null
@@ -0,0 +1,100 @@
+import words from './words'
+import { Util } from './util/Util'
+import { Convert } from './util/convert'
+
+const CryptoJS = require('crypto-js')
+
+export default class Bip39Mnemonic {
+
+       password: string
+
+       constructor(password: string) {
+               this.password = password
+       }
+
+       createWallet = (entropy: string): { mnemonic: string, seed: string } => {
+               if (entropy.length !== 32) {
+                       throw 'Invalid entropy length'
+               }
+
+               if (!entropy) {
+                       entropy = this.randomHex(64)
+               }
+
+               const entropyBinary = Convert.hexStringToBinary(entropy)
+               const entropySha256Binary = Convert.hexStringToBinary(this.calculateChecksum(entropy))
+               const entropyBinaryWithChecksum = entropyBinary + entropySha256Binary
+
+               const mnemonicWords = []
+               for (let i = 0; i < entropyBinaryWithChecksum.length; i += 11) {
+                       mnemonicWords.push(words[parseInt(entropyBinaryWithChecksum.substr(i, 11), 2)])
+               }
+
+               const mnemonicFinal = mnemonicWords.join(' ')
+               const seed = this.mnemonicToSeed(mnemonicFinal)
+
+               return {
+                       mnemonic: mnemonicFinal,
+                       seed,
+               }
+       }
+
+       validateMnemonic = (mnemonic: string): boolean => {
+               const wordArray = Util.normalizeUTF8(mnemonic).split(' ')
+               if (wordArray.length % 3 !== 0) {
+                       return false
+               }
+
+               const bits = wordArray.map((w: string) => {
+                       const wordIndex = words.indexOf(w)
+                       if (wordIndex === -1) {
+                               return false
+                       }
+                       return (wordIndex.toString(2)).padStart(11, '0')
+               }).join('')
+
+               const dividerIndex = Math.floor(bits.length / 33) * 32
+               const entropyBits = bits.slice(0, dividerIndex)
+               const checksumBits = bits.slice(dividerIndex)
+               const entropyBytes = entropyBits.match(/(.{1,8})/g).map((bin: string) => parseInt(bin, 2))
+
+               if (entropyBytes.length < 16) return false
+               if (entropyBytes.length > 32) return false
+               if (entropyBytes.length % 4 !== 0) return false
+
+               const entropyHex = Convert.bytesToHexString(entropyBytes)
+               const newChecksum = this.calculateChecksum(entropyHex)
+               const inputChecksum = Convert.binaryToHexString(checksumBits)
+
+               if (newChecksum != inputChecksum) {
+                       return false
+               }
+
+               return true
+       }
+
+       mnemonicToSeed = (mnemonic: string): string => {
+               const normalizedMnemonic = Util.normalizeUTF8(mnemonic)
+               const normalizedPassword = 'mnemonic' + Util.normalizeUTF8(this.password)
+
+               return CryptoJS.PBKDF2(
+                       normalizedMnemonic,
+                       normalizedPassword,
+                       {
+                               keySize: 512 / 32,
+                               iterations: 2048,
+                               hasher: CryptoJS.algo.SHA512,
+                       })
+                       .toString(CryptoJS.enc.Hex)
+       }
+
+       private randomHex = (length: number): string => {
+               return CryptoJS.lib.WordArray.random(length / 2).toString()
+       }
+
+       private calculateChecksum = (entropyHex: string): string => {
+               const entropySha256 = CryptoJS.SHA256(CryptoJS.enc.Hex.parse(entropyHex)).toString()
+               return entropySha256.substr(0, entropySha256.length / 32)
+       }
+
+}
diff --git a/lib/block-signer.ts b/lib/block-signer.ts
new file mode 100644 (file)
index 0000000..0ee3734
--- /dev/null
@@ -0,0 +1,118 @@
+import BigNumber from 'bignumber.js'
+import * as blake from 'blakejs'
+import { Ed25519 } from './ed25519'
+import { NanoAddress } from './nano-address'
+import NanoConverter from './nano-converter'
+import { Convert } from './util/convert'
+
+export default class BlockSigner {
+
+       nanoAddress = new NanoAddress()
+       ed25519 = new Ed25519()
+
+       preamble = 0x6.toString().padStart(64, '0')
+
+       send(data: SendBlock, privateKey: string) {
+               const balance = NanoConverter.convert(data.walletBalanceRaw, 'RAW', 'NANO')
+               const newBalance = new BigNumber(balance).minus(new BigNumber(data.amount))
+               const rawBalance = NanoConverter.convert(newBalance, 'NANO', 'RAW')
+               const hexBalance = Convert.dec2hex(rawBalance, 16).toUpperCase()
+               const account = this.nanoAddressToHexString(data.fromAddress)
+               const link = this.nanoAddressToHexString(data.toAddress)
+               const representative = this.nanoAddressToHexString(data.representativeAddress)
+
+               const signatureBytes = this.ed25519.sign(
+                       this.generateHash(this.preamble, account, data.frontier, representative, hexBalance, link),
+                       Convert.hex2ab(privateKey))
+
+               return {
+                       type: 'state',
+                       account: data.fromAddress,
+                       previous: data.frontier,
+                       representative: data.representativeAddress,
+                       balance: rawBalance,
+                       link,
+                       signature: Convert.ab2hex(signatureBytes),
+                       work: data.work,
+               }
+       }
+
+       receive(data: ReceiveBlock, privateKey: string) {
+               let balance = '0'
+               if (data.walletBalanceRaw != '0') {
+                       balance = NanoConverter.convert(data.walletBalanceRaw, 'RAW', 'NANO')
+               }
+
+               const amountNano = NanoConverter.convert(data.amount, 'RAW', 'NANO')
+               const newBalance = new BigNumber(balance).plus(new BigNumber(amountNano))
+               const rawBalance = NanoConverter.convert(newBalance, 'NANO', 'RAW')
+               const hexBalance = Convert.dec2hex(rawBalance, 16).toUpperCase()
+               const account = this.nanoAddressToHexString(data.walletAddress)
+               const representative = this.nanoAddressToHexString(data.representativeAddress)
+               const link = data.hash
+
+               const signatureBytes = this.ed25519.sign(
+                       this.generateHash(this.preamble, account, data.frontier, representative, hexBalance, link),
+                       Convert.hex2ab(privateKey))
+
+               return {
+                       type: 'state',
+                       account: data.walletAddress,
+                       previous: data.frontier,
+                       representative: data.representativeAddress,
+                       balance: rawBalance,
+                       link,
+                       signature: Convert.ab2hex(signatureBytes),
+                       work: data.work,
+               }
+       }
+
+       private generateHash(preamble: string, account: string, previous: string, representative: string, balance: string, link: string) {
+               const ctx = blake.blake2bInit(32, undefined)
+               blake.blake2bUpdate(ctx, Convert.hex2ab(preamble))
+               blake.blake2bUpdate(ctx, Convert.hex2ab(account))
+               blake.blake2bUpdate(ctx, Convert.hex2ab(previous))
+               blake.blake2bUpdate(ctx, Convert.hex2ab(representative))
+               blake.blake2bUpdate(ctx, Convert.hex2ab(balance))
+               blake.blake2bUpdate(ctx, Convert.hex2ab(link))
+               return blake.blake2bFinal(ctx)
+       }
+
+       private nanoAddressToHexString(addr: string): string {
+               addr = addr.slice(-60)
+               const isValid = /^[13456789abcdefghijkmnopqrstuwxyz]+$/.test(addr)
+               if (isValid) {
+                       const keyBytes = this.nanoAddress.decodeNanoBase32(addr.substring(0, 52))
+                       const hashBytes = this.nanoAddress.decodeNanoBase32(addr.substring(52, 60))
+                       const blakeHash = blake.blake2b(keyBytes, undefined, 5).reverse()
+                       if (Convert.ab2hex(hashBytes) == Convert.ab2hex(blakeHash)) {
+                               const key = Convert.ab2hex(keyBytes).toUpperCase()
+                               return key
+                       }
+                       throw 'Checksum mismatch'
+               } else {
+                       throw 'Illegal characters'
+               }
+       }
+
+}
+
+export interface SendBlock {
+       walletBalanceRaw: string
+       fromAddress: string
+       toAddress: string
+       representativeAddress: string
+       frontier: string
+       amount: string
+       work: string
+}
+
+export interface ReceiveBlock {
+       walletBalanceRaw: string
+       walletAddress: string
+       representativeAddress: string
+       frontier: string
+       hash: string
+       amount: string
+       work: string
+}
diff --git a/lib/ed25519.ts b/lib/ed25519.ts
new file mode 100644 (file)
index 0000000..9a11a22
--- /dev/null
@@ -0,0 +1,250 @@
+import * as blake from 'blakejs'
+import { Convert } from './util/convert'
+import { Curve25519 } from './util/curve25519'
+
+export class Ed25519 {
+
+       curve: Curve25519
+       X: Int32Array
+       Y: Int32Array
+       L: Uint8Array
+
+       constructor() {
+               this.curve = new Curve25519()
+               this.X = this.curve.gf([0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c, 0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, 0x36d3, 0x2169])
+               this.Y = this.curve.gf([0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666])
+               this.L = new Uint8Array([0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x10])
+       }
+
+       pack(r: Uint8Array, p: Int32Array[]): void {
+               const CURVE = this.curve
+               const tx = CURVE.gf(),
+                       ty = CURVE.gf(),
+                       zi = CURVE.gf()
+               CURVE.inv25519(zi, p[2])
+               CURVE.M(tx, p[0], zi)
+               CURVE.M(ty, p[1], zi)
+               CURVE.pack25519(r, ty)
+               r[31] ^= CURVE.par25519(tx) << 7
+       }
+
+       modL(r: Uint8Array, x: Uint32Array | Float64Array): void {
+               let carry, i, j, k
+               for (i = 63; i >= 32; --i) {
+                       carry = 0
+                       for (j = i - 32, k = i - 12; j < k; ++j) {
+                               x[j] += carry - 16 * x[i] * this.L[j - (i - 32)]
+                               carry = (x[j] + 128) >> 8
+                               x[j] -= carry * 256
+                       }
+                       x[j] += carry
+                       x[i] = 0
+               }
+
+               carry = 0
+               for (j = 0; j < 32; j++) {
+                       x[j] += carry - (x[31] >> 4) * this.L[j]
+                       carry = x[j] >> 8
+                       x[j] &= 255
+               }
+
+               for (j = 0; j < 32; j++) {
+                       x[j] -= carry * this.L[j]
+               }
+
+               for (i = 0; i < 32; i++) {
+                       x[i + 1] += x[i] >>> 8
+                       r[i] = x[i] & 0xff
+               }
+       }
+
+       reduce(r: Uint8Array): void {
+               const x = new Uint32Array(64)
+               for (let i = 0; i < 64; i++) {
+                       x[i] = r[i]
+               }
+
+               this.modL(r, x)
+       }
+
+       scalarmult(p: Int32Array[], q: Int32Array[], s: Uint8Array): void {
+               const CURVE = this.curve
+               CURVE.set25519(p[0], CURVE.gf0)
+               CURVE.set25519(p[1], CURVE.gf1)
+               CURVE.set25519(p[2], CURVE.gf1)
+               CURVE.set25519(p[3], CURVE.gf0)
+               for (let i = 255; i >= 0; --i) {
+                       const b = (s[(i / 8) | 0] >>> (i & 7)) & 1
+                       CURVE.cswap(p, q, b)
+                       CURVE.add(q, p)
+                       CURVE.add(p, p)
+                       CURVE.cswap(p, q, b)
+               }
+       }
+
+       scalarbase(p: Int32Array[], s: Uint8Array): void {
+               const CURVE = this.curve
+               const q = [CURVE.gf(), CURVE.gf(), CURVE.gf(), CURVE.gf()]
+               CURVE.set25519(q[0], this.X)
+               CURVE.set25519(q[1], this.Y)
+               CURVE.set25519(q[2], CURVE.gf1)
+               CURVE.M(q[3], this.X, this.Y)
+               this.scalarmult(p, q, s)
+       }
+
+       /**
+        * Generate an ed25519 keypair
+        * @param {String} seed A 32 byte cryptographic secure random hexadecimal string. This is basically the secret key
+        * @param {Object} Returns sk (Secret key) and pk (Public key) as 32 byte hexadecimal strings
+        */
+       generateKeys(seed: string): { privateKey: string, publicKey: string } {
+               const pk = new Uint8Array(32)
+               const p = [this.curve.gf(), this.curve.gf(), this.curve.gf(), this.curve.gf()]
+               const h = blake
+                       .blake2b(Convert.hex2ab(seed), undefined, 64)
+                       .slice(0, 32)
+
+               h[0] &= 0xf8
+               h[31] &= 0x7f
+               h[31] |= 0x40
+
+               this.scalarbase(p, h)
+               this.pack(pk, p)
+
+               return {
+                       privateKey: seed,
+                       publicKey: Convert.ab2hex(pk),
+               }
+       }
+
+       /**
+        * Generate a message signature
+        * @param {Uint8Array} msg Message to be signed as byte array
+        * @param {Uint8Array} secretKey Secret key as byte array
+        * @param {Uint8Array} Returns the signature as 64 byte typed array
+        */
+       sign(msg: Uint8Array, secretKey: Uint8Array): Uint8Array {
+               const signedMsg = this.naclSign(msg, secretKey)
+               const sig = new Uint8Array(64)
+
+               for (let i = 0; i < sig.length; i++) {
+                       sig[i] = signedMsg[i]
+               }
+
+               return sig
+       }
+
+       private naclSign(msg: Uint8Array, secretKey: Uint8Array): Uint8Array {
+               if (secretKey.length !== 32) {
+                       throw new Error('bad secret key size')
+               }
+
+               const signedMsg = new Uint8Array(64 + msg.length)
+               this.cryptoSign(signedMsg, msg, msg.length, secretKey)
+
+               return signedMsg
+       }
+
+       private cryptoSign(sm: Uint8Array, m: Uint8Array, n: number, sk: Uint8Array): number {
+               const CURVE = this.curve
+               const d = new Uint8Array(64)
+               const h = new Uint8Array(64)
+               const r = new Uint8Array(64)
+               const x = new Float64Array(64)
+               const p = [CURVE.gf(), CURVE.gf(), CURVE.gf(), CURVE.gf()]
+
+               let i
+               let j
+
+               const pk = Convert.hex2ab(this.generateKeys(Convert.ab2hex(sk)).publicKey)
+
+               this.cryptoHash(d, sk, 32)
+               d[0] &= 248
+               d[31] &= 127
+               d[31] |= 64
+
+               const smlen = n + 64
+               for (i = 0; i < n; i++) {
+                       sm[64 + i] = m[i]
+               }
+
+               for (i = 0; i < 32; i++) {
+                       sm[32 + i] = d[32 + i]
+               }
+
+               this.cryptoHash(r, sm.subarray(32), n + 32)
+               this.reduce(r)
+               this.scalarbase(p, r)
+               this.pack(sm, p)
+
+               for (i = 32; i < 64; i++) {
+                       sm[i] = pk[i - 32]
+               }
+
+               this.cryptoHash(h, sm, n + 64)
+               this.reduce(h)
+
+               for (i = 0; i < 64; i++) {
+                       x[i] = 0
+               }
+
+               for (i = 0; i < 32; i++) {
+                       x[i] = r[i]
+               }
+
+               for (i = 0; i < 32; i++) {
+                       for (j = 0; j < 32; j++) {
+                               x[i + j] += h[i] * d[j]
+                       }
+               }
+
+               this.modL(sm.subarray(32), x)
+
+               return smlen
+       }
+
+       private cryptoHash(out: Uint8Array, m: Uint8Array, n: number): number {
+               const input = new Uint8Array(n)
+               for (let i = 0; i < n; ++i) {
+                       input[i] = m[i]
+               }
+
+               const hash = blake.blake2b(input)
+               for (let i = 0; i < 64; ++i) {
+                       out[i] = hash[i]
+               }
+
+               return 0
+       }
+
+       /**
+        * TODO: Replace sha512 with blakejs
+        * Verify a message signature
+        * @param {Uint8Array} msg Message to be signed as byte array
+        * @param {Uint8Array} pk Public key as 32 byte array
+        * @param {Uint8Array} sig Signature as 64 byte array
+        * @param {Boolean} Returns true if signature is valid
+        */
+       // verify(msg, pk, sig) {
+       //      let CURVE = this.curve
+       //      let p = [CURVE.gf(), CURVE.gf(), CURVE.gf(), CURVE.gf()],
+       //              q = [CURVE.gf(), CURVE.gf(), CURVE.gf(), CURVE.gf()]
+
+       //      if (sig.length !== 64) return false
+       //      if (pk.length !== 32) return false
+       //      if (CURVE.unpackNeg(q, pk)) return false
+
+       //      // compute k = SHA512(R || A || M)
+       //      let k = this.sha512.init().update(sig.subarray(0, 32)).update(pk).digest(msg)
+       //      this.reduce(k)
+       //      this.scalarmult(p, q, k)
+
+       //      let t = new Uint8Array(32)
+       //      this.scalarbase(q, sig.subarray(32))
+       //      CURVE.add(p, q)
+       //      this.pack(t, p)
+
+       //      return Util.compare(sig.subarray(0, 32), t)
+       // }
+
+}
diff --git a/lib/nano-address.ts b/lib/nano-address.ts
new file mode 100644 (file)
index 0000000..6db5c9c
--- /dev/null
@@ -0,0 +1,88 @@
+import * as blake from 'blakejs'
+import { Convert } from './util/convert'
+
+export class NanoAddress {
+
+       readonly alphabet = '13456789abcdefghijkmnopqrstuwxyz'
+       readonly prefix = 'nano_'
+
+       deriveAddress = (publicKey: string): string => {
+               const publicKeyBytes = Convert.hex2ab(publicKey)
+               const checksum = blake
+                       .blake2b(publicKeyBytes, undefined, 5)
+                       .reverse()
+
+               const encoded = this.encodeNanoBase32(publicKeyBytes)
+               const encodedChecksum = this.encodeNanoBase32(checksum)
+
+               return this.prefix + encoded + encodedChecksum
+       }
+
+       encodeNanoBase32 = (publicKey: Uint8Array): string => {
+               const length = publicKey.length
+               const leftover = (length * 8) % 5
+               const offset = leftover === 0 ? 0 : 5 - leftover
+
+               let value = 0
+               let output = ''
+               let bits = 0
+
+               for (let i = 0; i < length; i++) {
+                       value = (value << 8) | publicKey[i]
+                       bits += 8
+
+                       while (bits >= 5) {
+                               output += this.alphabet[(value >>> (bits + offset - 5)) & 31]
+                               bits -= 5
+                       }
+               }
+
+               if (bits > 0) {
+                       output += this.alphabet[(value << (5 - (bits + offset))) & 31]
+               }
+
+               return output
+       }
+
+       decodeNanoBase32 = (input: string): Uint8Array => {
+               const length = input.length
+               const leftover = (length * 5) % 8
+               const offset = leftover === 0 ? 0 : 8 - leftover
+
+               let bits = 0
+               let value = 0
+               let index = 0
+               let output = new Uint8Array(Math.ceil((length * 5) / 8))
+
+               for (let i = 0; i < length; i++) {
+                       value = (value << 5) | this.readChar(input[i])
+                       bits += 5
+
+                       if (bits >= 8) {
+                               output[index++] = (value >>> (bits + offset - 8)) & 255
+                               bits -= 8
+                       }
+               }
+
+               if (bits > 0) {
+                       output[index++] = (value << (bits + offset - 8)) & 255
+               }
+
+               if (leftover !== 0) {
+                       output = output.slice(1)
+               }
+
+               return output
+       }
+
+       readChar(char: string): number {
+               const idx = this.alphabet.indexOf(char)
+
+               if (idx === -1) {
+                       throw `Invalid character found: ${char}`
+               }
+
+               return idx
+       }
+
+}
diff --git a/lib/nano-converter.ts b/lib/nano-converter.ts
new file mode 100644 (file)
index 0000000..6f8ece3
--- /dev/null
@@ -0,0 +1,48 @@
+import BigNumber from 'bignumber.js'
+
+export default class NanoConverter {
+
+       /**
+        * Converts the input value to the wanted unit
+        *
+        * @param input {BigNumber} value
+        * @param inputUnit {Unit} the unit to convert from
+        * @param outputUnit {Unit} the unit to convert to
+        */
+       static convert(input: BigNumber, inputUnit: string, outputUnit: string): string {
+               let value = new BigNumber(input.toString())
+
+               switch (inputUnit) {
+                       case 'RAW':
+                               value = value
+                               break
+                       case 'NANO':
+                       case 'MRAI':
+                               value = value.shiftedBy(30)
+                               break
+                       case 'KRAI':
+                               value = value.shiftedBy(27)
+                               break
+                       case 'RAI':
+                               value = value.shiftedBy(24)
+                               break
+                       default:
+                               throw `Unkown input unit ${inputUnit}, expected one of the following: RAW, NANO, MRAI, KRAI, RAI`
+               }
+
+               switch (outputUnit) {
+                       case 'RAW':
+                               return value.toFixed(0)
+                       case 'NANO':
+                       case 'MRAI':
+                               return value.shiftedBy(-30).toFixed(15, 1)
+                       case 'KRAI':
+                               return value.shiftedBy(-27).toFixed(12, 1)
+                       case 'RAI':
+                               return value.shiftedBy(-24).toFixed(9, 1)
+                       default:
+                               throw `Unknown output unit ${outputUnit}, expected one of the following: RAW, NANO, MRAI, KRAI, RAI`
+               }
+       }
+
+}
diff --git a/lib/util/convert.ts b/lib/util/convert.ts
new file mode 100644 (file)
index 0000000..490f6f0
--- /dev/null
@@ -0,0 +1,118 @@
+export class Convert {
+
+       /**
+        * Convert a string (UTF-8 encoded) to a byte array
+        *
+        * @param {String} str UTF-8 encoded string
+        * @return {Uint8Array} Byte array
+        */
+       static str2bin(str: string): Uint8Array {
+               str = str.replace(/\r\n/g, '\n')
+               const bin = new Uint8Array(str.length * 3)
+               let p = 0
+               for (let i = 0, len = str.length; i < len; i++) {
+                       const c = str.charCodeAt(i)
+                       if (c < 128) {
+                               bin[p++] = c
+                       } else if (c < 2048) {
+                               bin[p++] = (c >>> 6) | 192
+                               bin[p++] = (c & 63) | 128
+                       } else {
+                               bin[p++] = (c >>> 12) | 224
+                               bin[p++] = ((c >>> 6) & 63) | 128
+                               bin[p++] = (c & 63) | 128
+                       }
+               }
+               return bin.subarray(0, p)
+       }
+
+       /**
+        * Convert Array of 8 bytes (int64) to hex string
+        *
+        * @param {Uint8Array} bin Array of bytes
+        * @return {String} Hex encoded string
+        */
+       static ab2hex = (buf: ArrayBuffer): string => {
+               return Array.prototype.map.call(new Uint8Array(buf), x => ('00' + x.toString(16)).slice(-2)).join('')
+       }
+
+       /**
+        * Convert hex string to array of 8 bytes (int64)
+        *
+        * @param {String} bin Array of bytes
+        * @return {Uint8Array} Array of 8 bytes (int64)
+        */
+       static hex2ab = (hex: string): Uint8Array => {
+               const ab = []
+               for (let i = 0; i < hex.length; i += 2) {
+                       ab.push(parseInt(hex.substr(i, 2), 16))
+               }
+               return new Uint8Array(ab)
+       }
+
+       /**
+        * Convert a decimal number to hex string
+        *
+        * @param {String} str Decimal to be converted
+        * @param {Number} bytes Length of the output to be padded
+        * @returns Hexadecimal representation of the inputed decimal
+        */
+       static dec2hex = (str: number | string, bytes: number): string => {
+               const decimals = str.toString().split('')
+               const sum = []
+               let hex = []
+               let i: number
+               let s: number
+
+               while (decimals.length) {
+                       const dec = decimals.shift()
+                       if (!dec) {
+                               throw 'Invalid decimal'
+                       }
+
+                       s = 1 * +dec
+                       for (i = 0; s || i < sum.length; i++) {
+                               s += (sum[i] || 0) * 10
+                               sum[i] = s % 16
+                               s = (s - sum[i]) / 16
+                       }
+               }
+
+               while (sum.length) {
+                       const dec = sum.pop()
+                       if (!dec) {
+                               throw 'Invalid decimal'
+                       }
+
+                       hex.push(dec.toString(16))
+               }
+
+               let joined = hex.join('')
+
+               if (joined.length % 2 != 0) {
+                       joined = '0' + joined
+               }
+
+               if (bytes > joined.length / 2) {
+                       const diff = bytes - joined.length / 2
+                       for (let i = 0; i < diff; i++) {
+                               joined = '00' + joined
+                       }
+               }
+
+               return joined
+       }
+
+       static bytesToHexString = (bytes: number[]): string => {
+               return [...bytes].map(b => b.toString(16).padStart(2, '0')).join('')
+       }
+
+       static hexStringToBinary = (hex: string): string => {
+               return [...hex].map(c => (parseInt(c, 16).toString(2)).padStart(4, '0')).join('')
+       }
+
+       static binaryToHexString = (bin: string): string => {
+               return parseInt(bin, 2).toString(16)
+       }
+
+}
diff --git a/lib/util/curve25519.ts b/lib/util/curve25519.ts
new file mode 100644 (file)
index 0000000..21ed61f
--- /dev/null
@@ -0,0 +1,711 @@
+import { Util } from './util'
+
+export class Curve25519 {
+
+       gf0: Int32Array
+       gf1: Int32Array
+       D: Int32Array
+       D2: Int32Array
+       I: Int32Array
+       _9: Uint8Array
+       _121665: Int32Array
+
+       constructor() {
+               this.gf0 = this.gf()
+               this.gf1 = this.gf([1])
+               this._9 = new Uint8Array(32)
+               this._9[0] = 9
+               this._121665 = this.gf([0xdb41, 1])
+               this.D = this.gf([0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070, 0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203])
+               this.D2 = this.gf([0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0, 0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, 0xd9dc, 0x2406])
+               this.I = this.gf([0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83])
+       }
+
+       gf(init?: number[]): Int32Array {
+               const r = new Int32Array(16)
+               if (init) {
+                       for (let i = 0; i < init.length; i++) {
+                               r[i] = init[i]
+                       }
+               }
+
+               return r
+       }
+
+       A(o: Int32Array, a: Int32Array, b: Int32Array): void {
+               for (let i = 0; i < 16; i++) {
+                       o[i] = a[i] + b[i]
+               }
+       }
+
+       Z(o: Int32Array, a: Int32Array, b: Int32Array): void {
+               for (let i = 0; i < 16; i++) {
+                       o[i] = a[i] - b[i]
+               }
+       }
+
+       // Avoid loops for better performance
+       M(o: Int32Array, a: Int32Array, b: Int32Array): void {
+               let v, c,
+                       t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0, t6 = 0, t7 = 0,
+                       t8 = 0, t9 = 0, t10 = 0, t11 = 0, t12 = 0, t13 = 0, t14 = 0, t15 = 0,
+                       t16 = 0, t17 = 0, t18 = 0, t19 = 0, t20 = 0, t21 = 0, t22 = 0, t23 = 0,
+                       t24 = 0, t25 = 0, t26 = 0, t27 = 0, t28 = 0, t29 = 0, t30 = 0
+               const b0 = b[0],
+                       b1 = b[1],
+                       b2 = b[2],
+                       b3 = b[3],
+                       b4 = b[4],
+                       b5 = b[5],
+                       b6 = b[6],
+                       b7 = b[7],
+                       b8 = b[8],
+                       b9 = b[9],
+                       b10 = b[10],
+                       b11 = b[11],
+                       b12 = b[12],
+                       b13 = b[13],
+                       b14 = b[14],
+                       b15 = b[15]
+
+               v = a[0]
+               t0 += v * b0
+               t1 += v * b1
+               t2 += v * b2
+               t3 += v * b3
+               t4 += v * b4
+               t5 += v * b5
+               t6 += v * b6
+               t7 += v * b7
+               t8 += v * b8
+               t9 += v * b9
+               t10 += v * b10
+               t11 += v * b11
+               t12 += v * b12
+               t13 += v * b13
+               t14 += v * b14
+               t15 += v * b15
+               v = a[1]
+               t1 += v * b0
+               t2 += v * b1
+               t3 += v * b2
+               t4 += v * b3
+               t5 += v * b4
+               t6 += v * b5
+               t7 += v * b6
+               t8 += v * b7
+               t9 += v * b8
+               t10 += v * b9
+               t11 += v * b10
+               t12 += v * b11
+               t13 += v * b12
+               t14 += v * b13
+               t15 += v * b14
+               t16 += v * b15
+               v = a[2]
+               t2 += v * b0
+               t3 += v * b1
+               t4 += v * b2
+               t5 += v * b3
+               t6 += v * b4
+               t7 += v * b5
+               t8 += v * b6
+               t9 += v * b7
+               t10 += v * b8
+               t11 += v * b9
+               t12 += v * b10
+               t13 += v * b11
+               t14 += v * b12
+               t15 += v * b13
+               t16 += v * b14
+               t17 += v * b15
+               v = a[3]
+               t3 += v * b0
+               t4 += v * b1
+               t5 += v * b2
+               t6 += v * b3
+               t7 += v * b4
+               t8 += v * b5
+               t9 += v * b6
+               t10 += v * b7
+               t11 += v * b8
+               t12 += v * b9
+               t13 += v * b10
+               t14 += v * b11
+               t15 += v * b12
+               t16 += v * b13
+               t17 += v * b14
+               t18 += v * b15
+               v = a[4]
+               t4 += v * b0
+               t5 += v * b1
+               t6 += v * b2
+               t7 += v * b3
+               t8 += v * b4
+               t9 += v * b5
+               t10 += v * b6
+               t11 += v * b7
+               t12 += v * b8
+               t13 += v * b9
+               t14 += v * b10
+               t15 += v * b11
+               t16 += v * b12
+               t17 += v * b13
+               t18 += v * b14
+               t19 += v * b15
+               v = a[5]
+               t5 += v * b0
+               t6 += v * b1
+               t7 += v * b2
+               t8 += v * b3
+               t9 += v * b4
+               t10 += v * b5
+               t11 += v * b6
+               t12 += v * b7
+               t13 += v * b8
+               t14 += v * b9
+               t15 += v * b10
+               t16 += v * b11
+               t17 += v * b12
+               t18 += v * b13
+               t19 += v * b14
+               t20 += v * b15
+               v = a[6]
+               t6 += v * b0
+               t7 += v * b1
+               t8 += v * b2
+               t9 += v * b3
+               t10 += v * b4
+               t11 += v * b5
+               t12 += v * b6
+               t13 += v * b7
+               t14 += v * b8
+               t15 += v * b9
+               t16 += v * b10
+               t17 += v * b11
+               t18 += v * b12
+               t19 += v * b13
+               t20 += v * b14
+               t21 += v * b15
+               v = a[7]
+               t7 += v * b0
+               t8 += v * b1
+               t9 += v * b2
+               t10 += v * b3
+               t11 += v * b4
+               t12 += v * b5
+               t13 += v * b6
+               t14 += v * b7
+               t15 += v * b8
+               t16 += v * b9
+               t17 += v * b10
+               t18 += v * b11
+               t19 += v * b12
+               t20 += v * b13
+               t21 += v * b14
+               t22 += v * b15
+               v = a[8]
+               t8 += v * b0
+               t9 += v * b1
+               t10 += v * b2
+               t11 += v * b3
+               t12 += v * b4
+               t13 += v * b5
+               t14 += v * b6
+               t15 += v * b7
+               t16 += v * b8
+               t17 += v * b9
+               t18 += v * b10
+               t19 += v * b11
+               t20 += v * b12
+               t21 += v * b13
+               t22 += v * b14
+               t23 += v * b15
+               v = a[9]
+               t9 += v * b0
+               t10 += v * b1
+               t11 += v * b2
+               t12 += v * b3
+               t13 += v * b4
+               t14 += v * b5
+               t15 += v * b6
+               t16 += v * b7
+               t17 += v * b8
+               t18 += v * b9
+               t19 += v * b10
+               t20 += v * b11
+               t21 += v * b12
+               t22 += v * b13
+               t23 += v * b14
+               t24 += v * b15
+               v = a[10]
+               t10 += v * b0
+               t11 += v * b1
+               t12 += v * b2
+               t13 += v * b3
+               t14 += v * b4
+               t15 += v * b5
+               t16 += v * b6
+               t17 += v * b7
+               t18 += v * b8
+               t19 += v * b9
+               t20 += v * b10
+               t21 += v * b11
+               t22 += v * b12
+               t23 += v * b13
+               t24 += v * b14
+               t25 += v * b15
+               v = a[11]
+               t11 += v * b0
+               t12 += v * b1
+               t13 += v * b2
+               t14 += v * b3
+               t15 += v * b4
+               t16 += v * b5
+               t17 += v * b6
+               t18 += v * b7
+               t19 += v * b8
+               t20 += v * b9
+               t21 += v * b10
+               t22 += v * b11
+               t23 += v * b12
+               t24 += v * b13
+               t25 += v * b14
+               t26 += v * b15
+               v = a[12]
+               t12 += v * b0
+               t13 += v * b1
+               t14 += v * b2
+               t15 += v * b3
+               t16 += v * b4
+               t17 += v * b5
+               t18 += v * b6
+               t19 += v * b7
+               t20 += v * b8
+               t21 += v * b9
+               t22 += v * b10
+               t23 += v * b11
+               t24 += v * b12
+               t25 += v * b13
+               t26 += v * b14
+               t27 += v * b15
+               v = a[13]
+               t13 += v * b0
+               t14 += v * b1
+               t15 += v * b2
+               t16 += v * b3
+               t17 += v * b4
+               t18 += v * b5
+               t19 += v * b6
+               t20 += v * b7
+               t21 += v * b8
+               t22 += v * b9
+               t23 += v * b10
+               t24 += v * b11
+               t25 += v * b12
+               t26 += v * b13
+               t27 += v * b14
+               t28 += v * b15
+               v = a[14]
+               t14 += v * b0
+               t15 += v * b1
+               t16 += v * b2
+               t17 += v * b3
+               t18 += v * b4
+               t19 += v * b5
+               t20 += v * b6
+               t21 += v * b7
+               t22 += v * b8
+               t23 += v * b9
+               t24 += v * b10
+               t25 += v * b11
+               t26 += v * b12
+               t27 += v * b13
+               t28 += v * b14
+               t29 += v * b15
+               v = a[15]
+               t15 += v * b0
+               t16 += v * b1
+               t17 += v * b2
+               t18 += v * b3
+               t19 += v * b4
+               t20 += v * b5
+               t21 += v * b6
+               t22 += v * b7
+               t23 += v * b8
+               t24 += v * b9
+               t25 += v * b10
+               t26 += v * b11
+               t27 += v * b12
+               t28 += v * b13
+               t29 += v * b14
+               t30 += v * b15
+
+               t0 += 38 * t16
+               t1 += 38 * t17
+               t2 += 38 * t18
+               t3 += 38 * t19
+               t4 += 38 * t20
+               t5 += 38 * t21
+               t6 += 38 * t22
+               t7 += 38 * t23
+               t8 += 38 * t24
+               t9 += 38 * t25
+               t10 += 38 * t26
+               t11 += 38 * t27
+               t12 += 38 * t28
+               t13 += 38 * t29
+               t14 += 38 * t30
+
+               c = 1
+               v = t0 + c + 65535; c = Math.floor(v / 65536); t0 = v - c * 65536
+               v = t1 + c + 65535; c = Math.floor(v / 65536); t1 = v - c * 65536
+               v = t2 + c + 65535; c = Math.floor(v / 65536); t2 = v - c * 65536
+               v = t3 + c + 65535; c = Math.floor(v / 65536); t3 = v - c * 65536
+               v = t4 + c + 65535; c = Math.floor(v / 65536); t4 = v - c * 65536
+               v = t5 + c + 65535; c = Math.floor(v / 65536); t5 = v - c * 65536
+               v = t6 + c + 65535; c = Math.floor(v / 65536); t6 = v - c * 65536
+               v = t7 + c + 65535; c = Math.floor(v / 65536); t7 = v - c * 65536
+               v = t8 + c + 65535; c = Math.floor(v / 65536); t8 = v - c * 65536
+               v = t9 + c + 65535; c = Math.floor(v / 65536); t9 = v - c * 65536
+               v = t10 + c + 65535; c = Math.floor(v / 65536); t10 = v - c * 65536
+               v = t11 + c + 65535; c = Math.floor(v / 65536); t11 = v - c * 65536
+               v = t12 + c + 65535; c = Math.floor(v / 65536); t12 = v - c * 65536
+               v = t13 + c + 65535; c = Math.floor(v / 65536); t13 = v - c * 65536
+               v = t14 + c + 65535; c = Math.floor(v / 65536); t14 = v - c * 65536
+               v = t15 + c + 65535; c = Math.floor(v / 65536); t15 = v - c * 65536
+               t0 += c - 1 + 37 * (c - 1)
+
+               c = 1
+               v = t0 + c + 65535; c = Math.floor(v / 65536); t0 = v - c * 65536
+               v = t1 + c + 65535; c = Math.floor(v / 65536); t1 = v - c * 65536
+               v = t2 + c + 65535; c = Math.floor(v / 65536); t2 = v - c * 65536
+               v = t3 + c + 65535; c = Math.floor(v / 65536); t3 = v - c * 65536
+               v = t4 + c + 65535; c = Math.floor(v / 65536); t4 = v - c * 65536
+               v = t5 + c + 65535; c = Math.floor(v / 65536); t5 = v - c * 65536
+               v = t6 + c + 65535; c = Math.floor(v / 65536); t6 = v - c * 65536
+               v = t7 + c + 65535; c = Math.floor(v / 65536); t7 = v - c * 65536
+               v = t8 + c + 65535; c = Math.floor(v / 65536); t8 = v - c * 65536
+               v = t9 + c + 65535; c = Math.floor(v / 65536); t9 = v - c * 65536
+               v = t10 + c + 65535; c = Math.floor(v / 65536); t10 = v - c * 65536
+               v = t11 + c + 65535; c = Math.floor(v / 65536); t11 = v - c * 65536
+               v = t12 + c + 65535; c = Math.floor(v / 65536); t12 = v - c * 65536
+               v = t13 + c + 65535; c = Math.floor(v / 65536); t13 = v - c * 65536
+               v = t14 + c + 65535; c = Math.floor(v / 65536); t14 = v - c * 65536
+               v = t15 + c + 65535; c = Math.floor(v / 65536); t15 = v - c * 65536
+               t0 += c - 1 + 37 * (c - 1)
+
+               o[0] = t0
+               o[1] = t1
+               o[2] = t2
+               o[3] = t3
+               o[4] = t4
+               o[5] = t5
+               o[6] = t6
+               o[7] = t7
+               o[8] = t8
+               o[9] = t9
+               o[10] = t10
+               o[11] = t11
+               o[12] = t12
+               o[13] = t13
+               o[14] = t14
+               o[15] = t15
+       }
+
+       S(o: Int32Array, a: Int32Array): void {
+               this.M(o, a, a)
+       }
+
+       add(p: Int32Array[], q: Int32Array[]): void {
+               const a = this.gf(), b = this.gf(), c = this.gf(),
+                       d = this.gf(), e = this.gf(), f = this.gf(),
+                       g = this.gf(), h = this.gf(), t = this.gf()
+
+               this.Z(a, p[1], p[0])
+               this.Z(t, q[1], q[0])
+               this.M(a, a, t)
+               this.A(b, p[0], p[1])
+               this.A(t, q[0], q[1])
+               this.M(b, b, t)
+               this.M(c, p[3], q[3])
+               this.M(c, c, this.D2)
+               this.M(d, p[2], q[2])
+               this.A(d, d, d)
+               this.Z(e, b, a)
+               this.Z(f, d, c)
+               this.A(g, d, c)
+               this.A(h, b, a)
+               this.M(p[0], e, f)
+               this.M(p[1], h, g)
+               this.M(p[2], g, f)
+               this.M(p[3], e, h)
+       }
+
+       set25519(r: Int32Array, a: Int32Array): void {
+               for (let i = 0; i < 16; i++) {
+                       r[i] = a[i]
+               }
+       }
+
+       car25519(o: Int32Array): void {
+               let i, v, c = 1
+               for (i = 0; i < 16; i++) {
+                       v = o[i] + c + 65535
+                       c = Math.floor(v / 65536)
+                       o[i] = v - c * 65536
+               }
+
+               o[0] += c - 1 + 37 * (c - 1)
+       }
+
+       // b is 0 or 1
+       sel25519(p: Int32Array, q: Int32Array, b: number): void {
+               let i, t
+               const c = ~(b - 1)
+               for (i = 0; i < 16; i++) {
+                       t = c & (p[i] ^ q[i])
+                       p[i] ^= t
+                       q[i] ^= t
+               }
+       }
+
+       inv25519(o: Int32Array, i: Int32Array): void {
+               let a
+               const c = this.gf()
+               for (a = 0; a < 16; a++) {
+                       c[a] = i[a]
+               }
+
+               for (a = 253; a >= 0; a--) {
+                       this.S(c, c)
+                       if (a !== 2 && a !== 4) {
+                               this.M(c, c, i)
+                       }
+               }
+
+               for (a = 0; a < 16; a++) {
+                       o[a] = c[a]
+               }
+       }
+
+       neq25519(a: Int32Array, b: Int32Array): boolean {
+               const c = new Uint8Array(32), d = new Uint8Array(32)
+               this.pack25519(c, a)
+               this.pack25519(d, b)
+               return !Util.compare(c, d)
+       }
+
+       par25519(a: Int32Array): number {
+               const d = new Uint8Array(32)
+               this.pack25519(d, a)
+               return d[0] & 1
+       }
+
+       pow2523(o: Int32Array, i: Int32Array): void {
+               let a
+               const c = this.gf()
+               for (a = 0; a < 16; a++) {
+                       c[a] = i[a]
+               }
+
+               for (a = 250; a >= 0; a--) {
+                       this.S(c, c)
+                       if (a !== 1) this.M(c, c, i)
+               }
+
+               for (a = 0; a < 16; a++) {
+                       o[a] = c[a]
+               }
+       }
+
+       cswap(p: Int32Array[], q: Int32Array[], b: number): void {
+               for (let i = 0; i < 4; i++) {
+                       this.sel25519(p[i], q[i], b)
+               }
+       }
+
+       pack25519(o: Uint8Array, n: Int32Array): void {
+               let i
+               const m = this.gf()
+               const t = this.gf()
+               for (i = 0; i < 16; i++) {
+                       t[i] = n[i]
+               }
+
+               this.car25519(t)
+               this.car25519(t)
+               this.car25519(t)
+               for (let j = 0; j < 2; j++) {
+                       m[0] = t[0] - 0xffed
+                       for (i = 1; i < 15; i++) {
+                               m[i] = t[i] - 0xffff - ((m[i - 1] >>> 16) & 1)
+                               m[i - 1] &= 0xffff
+                       }
+
+                       m[15] = t[15] - 0x7fff - ((m[14] >>> 16) & 1)
+                       const b = (m[15] >>> 16) & 1
+                       m[14] &= 0xffff
+                       this.sel25519(t, m, 1 - b)
+               }
+
+               for (i = 0; i < 16; i++) {
+                       o[2 * i] = t[i] & 0xff
+                       o[2 * i + 1] = t[i] >>> 8
+               }
+       }
+
+       unpack25519(o: Int32Array, n: Int32Array): void {
+               for (let i = 0; i < 16; i++) {
+                       o[i] = n[2 * i] + (n[2 * i + 1] << 8)
+               }
+
+               o[15] &= 0x7fff
+       }
+
+       unpackNeg(r: Int32Array[], p: Int32Array): number {
+               const t = this.gf(),
+                       chk = this.gf(),
+                       num = this.gf(),
+                       den = this.gf(),
+                       den2 = this.gf(),
+                       den4 = this.gf(),
+                       den6 = this.gf()
+
+               this.set25519(r[2], this.gf1)
+               this.unpack25519(r[1], p)
+               this.S(num, r[1])
+               this.M(den, num, this.D)
+               this.Z(num, num, r[2])
+               this.A(den, r[2], den)
+
+               this.S(den2, den)
+               this.S(den4, den2)
+               this.M(den6, den4, den2)
+               this.M(t, den6, num)
+               this.M(t, t, den)
+
+               this.pow2523(t, t)
+               this.M(t, t, num)
+               this.M(t, t, den)
+               this.M(t, t, den)
+               this.M(r[0], t, den)
+
+               this.S(chk, r[0])
+               this.M(chk, chk, den)
+               if (this.neq25519(chk, num)) {
+                       this.M(r[0], r[0], this.I)
+               }
+
+               this.S(chk, r[0])
+               this.M(chk, chk, den)
+               if (this.neq25519(chk, num)) {
+                       return -1
+               }
+
+               if (this.par25519(r[0]) === (p[31] >>> 7)) {
+                       this.Z(r[0], this.gf0, r[0])
+               }
+
+               this.M(r[3], r[0], r[1])
+
+               return 0
+       }
+
+       /**
+        * Internal scalar mult function
+        * @param {Uint8Array} q Result
+        * @param {Uint8Array} s Secret key
+        * @param {Uint8Array} p Public key
+        */
+       cryptoScalarmult(q: Uint8Array, s: Uint8Array, p: Uint8Array): void {
+               const x = new Int32Array(80)
+               let r, i
+               const a = this.gf(), b = this.gf(), c = this.gf(),
+                       d = this.gf(), e = this.gf(), f = this.gf()
+
+               this.unpack25519(x, p)
+               for (i = 0; i < 16; i++) {
+                       b[i] = x[i]
+                       d[i] = a[i] = c[i] = 0
+               }
+
+               a[0] = d[0] = 1
+               for (i = 254; i >= 0; --i) {
+                       r = (s[i >>> 3] >>> (i & 7)) & 1
+                       this.sel25519(a, b, r)
+                       this.sel25519(c, d, r)
+                       this.A(e, a, c)
+                       this.Z(a, a, c)
+                       this.A(c, b, d)
+                       this.Z(b, b, d)
+                       this.S(d, e)
+                       this.S(f, a)
+                       this.M(a, c, a)
+                       this.M(c, b, e)
+                       this.A(e, a, c)
+                       this.Z(a, a, c)
+                       this.S(b, a)
+                       this.Z(c, d, f)
+                       this.M(a, c, this._121665)
+                       this.A(a, a, d)
+                       this.M(c, c, a)
+                       this.M(a, d, f)
+                       this.M(d, b, x)
+                       this.S(b, e)
+                       this.sel25519(a, b, r)
+                       this.sel25519(c, d, r)
+               }
+
+               for (i = 0; i < 16; i++) {
+                       x[i + 16] = a[i]
+                       x[i + 32] = c[i]
+                       x[i + 48] = b[i]
+                       x[i + 64] = d[i]
+               }
+
+               const x32 = x.subarray(32)
+               const x16 = x.subarray(16)
+               this.inv25519(x32, x32)
+               this.M(x16, x16, x32)
+               this.pack25519(q, x16)
+       }
+
+       /**
+        * Generate the common key as the produkt of sk1 * pk2
+        * @param {Uint8Array} sk A 32 byte secret key of pair 1
+        * @param {Uint8Array} pk A 32 byte public key of pair 2
+        * @return {Uint8Array} sk * pk
+        */
+       scalarMult(sk: Uint8Array, pk: Uint8Array) {
+               const q = new Uint8Array(32)
+               this.cryptoScalarmult(q, sk, pk)
+
+               return q
+       }
+
+       /**
+        * Generate a curve 25519 keypair
+        * @param {Uint8Array} seed A 32 byte cryptographic secure random array. This is basically the secret key
+        * @param {Object} Returns sk (Secret key) and pk (Public key) as 32 byte typed arrays
+        */
+       generateKeys(seed: Uint8Array): { sk: Uint8Array, pk: Uint8Array } {
+               const sk = seed.slice()
+               const pk = new Uint8Array(32)
+               if (sk.length !== 32) {
+                       throw 'Invalid secret key size, expected 32 bytes'
+               }
+
+               sk[0] &= 0xf8
+               sk[31] &= 0x7f
+               sk[31] |= 0x40
+
+               this.cryptoScalarmult(pk, sk, this._9)
+
+               return {
+                       sk,
+                       pk,
+               }
+       }
+
+}
diff --git a/lib/util/util.ts b/lib/util/util.ts
new file mode 100644 (file)
index 0000000..bf0f721
--- /dev/null
@@ -0,0 +1,30 @@
+export class Util {
+
+       /**
+        * Time constant comparison of two arrays
+        *
+        * @param {Uint8Array} lh First array of bytes
+        * @param {Uint8Array} rh Second array of bytes
+        * @return {Boolean} True if the arrays are equal (length and content), false otherwise
+        */
+       static compare(lh: Uint8Array, rh: Uint8Array): boolean {
+               if (lh.length !== rh.length) {
+                       return false
+               }
+
+               let i
+               let d = 0
+               const len = lh.length
+
+               for (i = 0; i < len; i++) {
+                       d |= lh[i] ^ rh[i]
+               }
+
+               return d === 0
+       }
+
+       static normalizeUTF8 = (str: string): string => {
+               return str ? str.normalize('NFKD') : ''
+       }
+
+}
\ No newline at end of file
diff --git a/lib/words.ts b/lib/words.ts
new file mode 100644 (file)
index 0000000..2708a2a
--- /dev/null
@@ -0,0 +1,2054 @@
+const words = [
+
+       'abandon',
+       'ability',
+       'able',
+       'about',
+       'above',
+       'absent',
+       'absorb',
+       'abstract',
+       'absurd',
+       'abuse',
+       'access',
+       'accident',
+       'account',
+       'accuse',
+       'achieve',
+       'acid',
+       'acoustic',
+       'acquire',
+       'across',
+       'act',
+       'action',
+       'actor',
+       'actress',
+       'actual',
+       'adapt',
+       'add',
+       'addict',
+       'address',
+       'adjust',
+       'admit',
+       'adult',
+       'advance',
+       'advice',
+       'aerobic',
+       'affair',
+       'afford',
+       'afraid',
+       'again',
+       'age',
+       'agent',
+       'agree',
+       'ahead',
+       'aim',
+       'air',
+       'airport',
+       'aisle',
+       'alarm',
+       'album',
+       'alcohol',
+       'alert',
+       'alien',
+       'all',
+       'alley',
+       'allow',
+       'almost',
+       'alone',
+       'alpha',
+       'already',
+       'also',
+       'alter',
+       'always',
+       'amateur',
+       'amazing',
+       'among',
+       'amount',
+       'amused',
+       'analyst',
+       'anchor',
+       'ancient',
+       'anger',
+       'angle',
+       'angry',
+       'animal',
+       'ankle',
+       'announce',
+       'annual',
+       'another',
+       'answer',
+       'antenna',
+       'antique',
+       'anxiety',
+       'any',
+       'apart',
+       'apology',
+       'appear',
+       'apple',
+       'approve',
+       'april',
+       'arch',
+       'arctic',
+       'area',
+       'arena',
+       'argue',
+       'arm',
+       'armed',
+       'armor',
+       'army',
+       'around',
+       'arrange',
+       'arrest',
+       'arrive',
+       'arrow',
+       'art',
+       'artefact',
+       'artist',
+       'artwork',
+       'ask',
+       'aspect',
+       'assault',
+       'asset',
+       'assist',
+       'assume',
+       'asthma',
+       'athlete',
+       'atom',
+       'attack',
+       'attend',
+       'attitude',
+       'attract',
+       'auction',
+       'audit',
+       'august',
+       'aunt',
+       'author',
+       'auto',
+       'autumn',
+       'average',
+       'avocado',
+       'avoid',
+       'awake',
+       'aware',
+       'away',
+       'awesome',
+       'awful',
+       'awkward',
+       'axis',
+       'baby',
+       'bachelor',
+       'bacon',
+       'badge',
+       'bag',
+       'balance',
+       'balcony',
+       'ball',
+       'bamboo',
+       'banana',
+       'banner',
+       'bar',
+       'barely',
+       'bargain',
+       'barrel',
+       'base',
+       'basic',
+       'basket',
+       'battle',
+       'beach',
+       'bean',
+       'beauty',
+       'because',
+       'become',
+       'beef',
+       'before',
+       'begin',
+       'behave',
+       'behind',
+       'believe',
+       'below',
+       'belt',
+       'bench',
+       'benefit',
+       'best',
+       'betray',
+       'better',
+       'between',
+       'beyond',
+       'bicycle',
+       'bid',
+       'bike',
+       'bind',
+       'biology',
+       'bird',
+       'birth',
+       'bitter',
+       'black',
+       'blade',
+       'blame',
+       'blanket',
+       'blast',
+       'bleak',
+       'bless',
+       'blind',
+       'blood',
+       'blossom',
+       'blouse',
+       'blue',
+       'blur',
+       'blush',
+       'board',
+       'boat',
+       'body',
+       'boil',
+       'bomb',
+       'bone',
+       'bonus',
+       'book',
+       'boost',
+       'border',
+       'boring',
+       'borrow',
+       'boss',
+       'bottom',
+       'bounce',
+       'box',
+       'boy',
+       'bracket',
+       'brain',
+       'brand',
+       'brass',
+       'brave',
+       'bread',
+       'breeze',
+       'brick',
+       'bridge',
+       'brief',
+       'bright',
+       'bring',
+       'brisk',
+       'broccoli',
+       'broken',
+       'bronze',
+       'broom',
+       'brother',
+       'brown',
+       'brush',
+       'bubble',
+       'buddy',
+       'budget',
+       'buffalo',
+       'build',
+       'bulb',
+       'bulk',
+       'bullet',
+       'bundle',
+       'bunker',
+       'burden',
+       'burger',
+       'burst',
+       'bus',
+       'business',
+       'busy',
+       'butter',
+       'buyer',
+       'buzz',
+       'cabbage',
+       'cabin',
+       'cable',
+       'cactus',
+       'cage',
+       'cake',
+       'call',
+       'calm',
+       'camera',
+       'camp',
+       'can',
+       'canal',
+       'cancel',
+       'candy',
+       'cannon',
+       'canoe',
+       'canvas',
+       'canyon',
+       'capable',
+       'capital',
+       'captain',
+       'car',
+       'carbon',
+       'card',
+       'cargo',
+       'carpet',
+       'carry',
+       'cart',
+       'case',
+       'cash',
+       'casino',
+       'castle',
+       'casual',
+       'cat',
+       'catalog',
+       'catch',
+       'category',
+       'cattle',
+       'caught',
+       'cause',
+       'caution',
+       'cave',
+       'ceiling',
+       'celery',
+       'cement',
+       'census',
+       'century',
+       'cereal',
+       'certain',
+       'chair',
+       'chalk',
+       'champion',
+       'change',
+       'chaos',
+       'chapter',
+       'charge',
+       'chase',
+       'chat',
+       'cheap',
+       'check',
+       'cheese',
+       'chef',
+       'cherry',
+       'chest',
+       'chicken',
+       'chief',
+       'child',
+       'chimney',
+       'choice',
+       'choose',
+       'chronic',
+       'chuckle',
+       'chunk',
+       'churn',
+       'cigar',
+       'cinnamon',
+       'circle',
+       'citizen',
+       'city',
+       'civil',
+       'claim',
+       'clap',
+       'clarify',
+       'claw',
+       'clay',
+       'clean',
+       'clerk',
+       'clever',
+       'click',
+       'client',
+       'cliff',
+       'climb',
+       'clinic',
+       'clip',
+       'clock',
+       'clog',
+       'close',
+       'cloth',
+       'cloud',
+       'clown',
+       'club',
+       'clump',
+       'cluster',
+       'clutch',
+       'coach',
+       'coast',
+       'coconut',
+       'code',
+       'coffee',
+       'coil',
+       'coin',
+       'collect',
+       'color',
+       'column',
+       'combine',
+       'come',
+       'comfort',
+       'comic',
+       'common',
+       'company',
+       'concert',
+       'conduct',
+       'confirm',
+       'congress',
+       'connect',
+       'consider',
+       'control',
+       'convince',
+       'cook',
+       'cool',
+       'copper',
+       'copy',
+       'coral',
+       'core',
+       'corn',
+       'correct',
+       'cost',
+       'cotton',
+       'couch',
+       'country',
+       'couple',
+       'course',
+       'cousin',
+       'cover',
+       'coyote',
+       'crack',
+       'cradle',
+       'craft',
+       'cram',
+       'crane',
+       'crash',
+       'crater',
+       'crawl',
+       'crazy',
+       'cream',
+       'credit',
+       'creek',
+       'crew',
+       'cricket',
+       'crime',
+       'crisp',
+       'critic',
+       'crop',
+       'cross',
+       'crouch',
+       'crowd',
+       'crucial',
+       'cruel',
+       'cruise',
+       'crumble',
+       'crunch',
+       'crush',
+       'cry',
+       'crystal',
+       'cube',
+       'culture',
+       'cup',
+       'cupboard',
+       'curious',
+       'current',
+       'curtain',
+       'curve',
+       'cushion',
+       'custom',
+       'cute',
+       'cycle',
+       'dad',
+       'damage',
+       'damp',
+       'dance',
+       'danger',
+       'daring',
+       'dash',
+       'daughter',
+       'dawn',
+       'day',
+       'deal',
+       'debate',
+       'debris',
+       'decade',
+       'december',
+       'decide',
+       'decline',
+       'decorate',
+       'decrease',
+       'deer',
+       'defense',
+       'define',
+       'defy',
+       'degree',
+       'delay',
+       'deliver',
+       'demand',
+       'demise',
+       'denial',
+       'dentist',
+       'deny',
+       'depart',
+       'depend',
+       'deposit',
+       'depth',
+       'deputy',
+       'derive',
+       'describe',
+       'desert',
+       'design',
+       'desk',
+       'despair',
+       'destroy',
+       'detail',
+       'detect',
+       'develop',
+       'device',
+       'devote',
+       'diagram',
+       'dial',
+       'diamond',
+       'diary',
+       'dice',
+       'diesel',
+       'diet',
+       'differ',
+       'digital',
+       'dignity',
+       'dilemma',
+       'dinner',
+       'dinosaur',
+       'direct',
+       'dirt',
+       'disagree',
+       'discover',
+       'disease',
+       'dish',
+       'dismiss',
+       'disorder',
+       'display',
+       'distance',
+       'divert',
+       'divide',
+       'divorce',
+       'dizzy',
+       'doctor',
+       'document',
+       'dog',
+       'doll',
+       'dolphin',
+       'domain',
+       'donate',
+       'donkey',
+       'donor',
+       'door',
+       'dose',
+       'double',
+       'dove',
+       'draft',
+       'dragon',
+       'drama',
+       'drastic',
+       'draw',
+       'dream',
+       'dress',
+       'drift',
+       'drill',
+       'drink',
+       'drip',
+       'drive',
+       'drop',
+       'drum',
+       'dry',
+       'duck',
+       'dumb',
+       'dune',
+       'during',
+       'dust',
+       'dutch',
+       'duty',
+       'dwarf',
+       'dynamic',
+       'eager',
+       'eagle',
+       'early',
+       'earn',
+       'earth',
+       'easily',
+       'east',
+       'easy',
+       'echo',
+       'ecology',
+       'economy',
+       'edge',
+       'edit',
+       'educate',
+       'effort',
+       'egg',
+       'eight',
+       'either',
+       'elbow',
+       'elder',
+       'electric',
+       'elegant',
+       'element',
+       'elephant',
+       'elevator',
+       'elite',
+       'else',
+       'embark',
+       'embody',
+       'embrace',
+       'emerge',
+       'emotion',
+       'employ',
+       'empower',
+       'empty',
+       'enable',
+       'enact',
+       'end',
+       'endless',
+       'endorse',
+       'enemy',
+       'energy',
+       'enforce',
+       'engage',
+       'engine',
+       'enhance',
+       'enjoy',
+       'enlist',
+       'enough',
+       'enrich',
+       'enroll',
+       'ensure',
+       'enter',
+       'entire',
+       'entry',
+       'envelope',
+       'episode',
+       'equal',
+       'equip',
+       'era',
+       'erase',
+       'erode',
+       'erosion',
+       'error',
+       'erupt',
+       'escape',
+       'essay',
+       'essence',
+       'estate',
+       'eternal',
+       'ethics',
+       'evidence',
+       'evil',
+       'evoke',
+       'evolve',
+       'exact',
+       'example',
+       'excess',
+       'exchange',
+       'excite',
+       'exclude',
+       'excuse',
+       'execute',
+       'exercise',
+       'exhaust',
+       'exhibit',
+       'exile',
+       'exist',
+       'exit',
+       'exotic',
+       'expand',
+       'expect',
+       'expire',
+       'explain',
+       'expose',
+       'express',
+       'extend',
+       'extra',
+       'eye',
+       'eyebrow',
+       'fabric',
+       'face',
+       'faculty',
+       'fade',
+       'faint',
+       'faith',
+       'fall',
+       'false',
+       'fame',
+       'family',
+       'famous',
+       'fan',
+       'fancy',
+       'fantasy',
+       'farm',
+       'fashion',
+       'fat',
+       'fatal',
+       'father',
+       'fatigue',
+       'fault',
+       'favorite',
+       'feature',
+       'february',
+       'federal',
+       'fee',
+       'feed',
+       'feel',
+       'female',
+       'fence',
+       'festival',
+       'fetch',
+       'fever',
+       'few',
+       'fiber',
+       'fiction',
+       'field',
+       'figure',
+       'file',
+       'film',
+       'filter',
+       'final',
+       'find',
+       'fine',
+       'finger',
+       'finish',
+       'fire',
+       'firm',
+       'first',
+       'fiscal',
+       'fish',
+       'fit',
+       'fitness',
+       'fix',
+       'flag',
+       'flame',
+       'flash',
+       'flat',
+       'flavor',
+       'flee',
+       'flight',
+       'flip',
+       'float',
+       'flock',
+       'floor',
+       'flower',
+       'fluid',
+       'flush',
+       'fly',
+       'foam',
+       'focus',
+       'fog',
+       'foil',
+       'fold',
+       'follow',
+       'food',
+       'foot',
+       'force',
+       'forest',
+       'forget',
+       'fork',
+       'fortune',
+       'forum',
+       'forward',
+       'fossil',
+       'foster',
+       'found',
+       'fox',
+       'fragile',
+       'frame',
+       'frequent',
+       'fresh',
+       'friend',
+       'fringe',
+       'frog',
+       'front',
+       'frost',
+       'frown',
+       'frozen',
+       'fruit',
+       'fuel',
+       'fun',
+       'funny',
+       'furnace',
+       'fury',
+       'future',
+       'gadget',
+       'gain',
+       'galaxy',
+       'gallery',
+       'game',
+       'gap',
+       'garage',
+       'garbage',
+       'garden',
+       'garlic',
+       'garment',
+       'gas',
+       'gasp',
+       'gate',
+       'gather',
+       'gauge',
+       'gaze',
+       'general',
+       'genius',
+       'genre',
+       'gentle',
+       'genuine',
+       'gesture',
+       'ghost',
+       'giant',
+       'gift',
+       'giggle',
+       'ginger',
+       'giraffe',
+       'girl',
+       'give',
+       'glad',
+       'glance',
+       'glare',
+       'glass',
+       'glide',
+       'glimpse',
+       'globe',
+       'gloom',
+       'glory',
+       'glove',
+       'glow',
+       'glue',
+       'goat',
+       'goddess',
+       'gold',
+       'good',
+       'goose',
+       'gorilla',
+       'gospel',
+       'gossip',
+       'govern',
+       'gown',
+       'grab',
+       'grace',
+       'grain',
+       'grant',
+       'grape',
+       'grass',
+       'gravity',
+       'great',
+       'green',
+       'grid',
+       'grief',
+       'grit',
+       'grocery',
+       'group',
+       'grow',
+       'grunt',
+       'guard',
+       'guess',
+       'guide',
+       'guilt',
+       'guitar',
+       'gun',
+       'gym',
+       'habit',
+       'hair',
+       'half',
+       'hammer',
+       'hamster',
+       'hand',
+       'happy',
+       'harbor',
+       'hard',
+       'harsh',
+       'harvest',
+       'hat',
+       'have',
+       'hawk',
+       'hazard',
+       'head',
+       'health',
+       'heart',
+       'heavy',
+       'hedgehog',
+       'height',
+       'hello',
+       'helmet',
+       'help',
+       'hen',
+       'hero',
+       'hidden',
+       'high',
+       'hill',
+       'hint',
+       'hip',
+       'hire',
+       'history',
+       'hobby',
+       'hockey',
+       'hold',
+       'hole',
+       'holiday',
+       'hollow',
+       'home',
+       'honey',
+       'hood',
+       'hope',
+       'horn',
+       'horror',
+       'horse',
+       'hospital',
+       'host',
+       'hotel',
+       'hour',
+       'hover',
+       'hub',
+       'huge',
+       'human',
+       'humble',
+       'humor',
+       'hundred',
+       'hungry',
+       'hunt',
+       'hurdle',
+       'hurry',
+       'hurt',
+       'husband',
+       'hybrid',
+       'ice',
+       'icon',
+       'idea',
+       'identify',
+       'idle',
+       'ignore',
+       'ill',
+       'illegal',
+       'illness',
+       'image',
+       'imitate',
+       'immense',
+       'immune',
+       'impact',
+       'impose',
+       'improve',
+       'impulse',
+       'inch',
+       'include',
+       'income',
+       'increase',
+       'index',
+       'indicate',
+       'indoor',
+       'industry',
+       'infant',
+       'inflict',
+       'inform',
+       'inhale',
+       'inherit',
+       'initial',
+       'inject',
+       'injury',
+       'inmate',
+       'inner',
+       'innocent',
+       'input',
+       'inquiry',
+       'insane',
+       'insect',
+       'inside',
+       'inspire',
+       'install',
+       'intact',
+       'interest',
+       'into',
+       'invest',
+       'invite',
+       'involve',
+       'iron',
+       'island',
+       'isolate',
+       'issue',
+       'item',
+       'ivory',
+       'jacket',
+       'jaguar',
+       'jar',
+       'jazz',
+       'jealous',
+       'jeans',
+       'jelly',
+       'jewel',
+       'job',
+       'join',
+       'joke',
+       'journey',
+       'joy',
+       'judge',
+       'juice',
+       'jump',
+       'jungle',
+       'junior',
+       'junk',
+       'just',
+       'kangaroo',
+       'keen',
+       'keep',
+       'ketchup',
+       'key',
+       'kick',
+       'kid',
+       'kidney',
+       'kind',
+       'kingdom',
+       'kiss',
+       'kit',
+       'kitchen',
+       'kite',
+       'kitten',
+       'kiwi',
+       'knee',
+       'knife',
+       'knock',
+       'know',
+       'lab',
+       'label',
+       'labor',
+       'ladder',
+       'lady',
+       'lake',
+       'lamp',
+       'language',
+       'laptop',
+       'large',
+       'later',
+       'latin',
+       'laugh',
+       'laundry',
+       'lava',
+       'law',
+       'lawn',
+       'lawsuit',
+       'layer',
+       'lazy',
+       'leader',
+       'leaf',
+       'learn',
+       'leave',
+       'lecture',
+       'left',
+       'leg',
+       'legal',
+       'legend',
+       'leisure',
+       'lemon',
+       'lend',
+       'length',
+       'lens',
+       'leopard',
+       'lesson',
+       'letter',
+       'level',
+       'liar',
+       'liberty',
+       'library',
+       'license',
+       'life',
+       'lift',
+       'light',
+       'like',
+       'limb',
+       'limit',
+       'link',
+       'lion',
+       'liquid',
+       'list',
+       'little',
+       'live',
+       'lizard',
+       'load',
+       'loan',
+       'lobster',
+       'local',
+       'lock',
+       'logic',
+       'lonely',
+       'long',
+       'loop',
+       'lottery',
+       'loud',
+       'lounge',
+       'love',
+       'loyal',
+       'lucky',
+       'luggage',
+       'lumber',
+       'lunar',
+       'lunch',
+       'luxury',
+       'lyrics',
+       'machine',
+       'mad',
+       'magic',
+       'magnet',
+       'maid',
+       'mail',
+       'main',
+       'major',
+       'make',
+       'mammal',
+       'man',
+       'manage',
+       'mandate',
+       'mango',
+       'mansion',
+       'manual',
+       'maple',
+       'marble',
+       'march',
+       'margin',
+       'marine',
+       'market',
+       'marriage',
+       'mask',
+       'mass',
+       'master',
+       'match',
+       'material',
+       'math',
+       'matrix',
+       'matter',
+       'maximum',
+       'maze',
+       'meadow',
+       'mean',
+       'measure',
+       'meat',
+       'mechanic',
+       'medal',
+       'media',
+       'melody',
+       'melt',
+       'member',
+       'memory',
+       'mention',
+       'menu',
+       'mercy',
+       'merge',
+       'merit',
+       'merry',
+       'mesh',
+       'message',
+       'metal',
+       'method',
+       'middle',
+       'midnight',
+       'milk',
+       'million',
+       'mimic',
+       'mind',
+       'minimum',
+       'minor',
+       'minute',
+       'miracle',
+       'mirror',
+       'misery',
+       'miss',
+       'mistake',
+       'mix',
+       'mixed',
+       'mixture',
+       'mobile',
+       'model',
+       'modify',
+       'mom',
+       'moment',
+       'monitor',
+       'monkey',
+       'monster',
+       'month',
+       'moon',
+       'moral',
+       'more',
+       'morning',
+       'mosquito',
+       'mother',
+       'motion',
+       'motor',
+       'mountain',
+       'mouse',
+       'move',
+       'movie',
+       'much',
+       'muffin',
+       'mule',
+       'multiply',
+       'muscle',
+       'museum',
+       'mushroom',
+       'music',
+       'must',
+       'mutual',
+       'myself',
+       'mystery',
+       'myth',
+       'naive',
+       'name',
+       'napkin',
+       'narrow',
+       'nasty',
+       'nation',
+       'nature',
+       'near',
+       'neck',
+       'need',
+       'negative',
+       'neglect',
+       'neither',
+       'nephew',
+       'nerve',
+       'nest',
+       'net',
+       'network',
+       'neutral',
+       'never',
+       'news',
+       'next',
+       'nice',
+       'night',
+       'noble',
+       'noise',
+       'nominee',
+       'noodle',
+       'normal',
+       'north',
+       'nose',
+       'notable',
+       'note',
+       'nothing',
+       'notice',
+       'novel',
+       'now',
+       'nuclear',
+       'number',
+       'nurse',
+       'nut',
+       'oak',
+       'obey',
+       'object',
+       'oblige',
+       'obscure',
+       'observe',
+       'obtain',
+       'obvious',
+       'occur',
+       'ocean',
+       'october',
+       'odor',
+       'off',
+       'offer',
+       'office',
+       'often',
+       'oil',
+       'okay',
+       'old',
+       'olive',
+       'olympic',
+       'omit',
+       'once',
+       'one',
+       'onion',
+       'online',
+       'only',
+       'open',
+       'opera',
+       'opinion',
+       'oppose',
+       'option',
+       'orange',
+       'orbit',
+       'orchard',
+       'order',
+       'ordinary',
+       'organ',
+       'orient',
+       'original',
+       'orphan',
+       'ostrich',
+       'other',
+       'outdoor',
+       'outer',
+       'output',
+       'outside',
+       'oval',
+       'oven',
+       'over',
+       'own',
+       'owner',
+       'oxygen',
+       'oyster',
+       'ozone',
+       'pact',
+       'paddle',
+       'page',
+       'pair',
+       'palace',
+       'palm',
+       'panda',
+       'panel',
+       'panic',
+       'panther',
+       'paper',
+       'parade',
+       'parent',
+       'park',
+       'parrot',
+       'party',
+       'pass',
+       'patch',
+       'path',
+       'patient',
+       'patrol',
+       'pattern',
+       'pause',
+       'pave',
+       'payment',
+       'peace',
+       'peanut',
+       'pear',
+       'peasant',
+       'pelican',
+       'pen',
+       'penalty',
+       'pencil',
+       'people',
+       'pepper',
+       'perfect',
+       'permit',
+       'person',
+       'pet',
+       'phone',
+       'photo',
+       'phrase',
+       'physical',
+       'piano',
+       'picnic',
+       'picture',
+       'piece',
+       'pig',
+       'pigeon',
+       'pill',
+       'pilot',
+       'pink',
+       'pioneer',
+       'pipe',
+       'pistol',
+       'pitch',
+       'pizza',
+       'place',
+       'planet',
+       'plastic',
+       'plate',
+       'play',
+       'please',
+       'pledge',
+       'pluck',
+       'plug',
+       'plunge',
+       'poem',
+       'poet',
+       'point',
+       'polar',
+       'pole',
+       'police',
+       'pond',
+       'pony',
+       'pool',
+       'popular',
+       'portion',
+       'position',
+       'possible',
+       'post',
+       'potato',
+       'pottery',
+       'poverty',
+       'powder',
+       'power',
+       'practice',
+       'praise',
+       'predict',
+       'prefer',
+       'prepare',
+       'present',
+       'pretty',
+       'prevent',
+       'price',
+       'pride',
+       'primary',
+       'print',
+       'priority',
+       'prison',
+       'private',
+       'prize',
+       'problem',
+       'process',
+       'produce',
+       'profit',
+       'program',
+       'project',
+       'promote',
+       'proof',
+       'property',
+       'prosper',
+       'protect',
+       'proud',
+       'provide',
+       'public',
+       'pudding',
+       'pull',
+       'pulp',
+       'pulse',
+       'pumpkin',
+       'punch',
+       'pupil',
+       'puppy',
+       'purchase',
+       'purity',
+       'purpose',
+       'purse',
+       'push',
+       'put',
+       'puzzle',
+       'pyramid',
+       'quality',
+       'quantum',
+       'quarter',
+       'question',
+       'quick',
+       'quit',
+       'quiz',
+       'quote',
+       'rabbit',
+       'raccoon',
+       'race',
+       'rack',
+       'radar',
+       'radio',
+       'rail',
+       'rain',
+       'raise',
+       'rally',
+       'ramp',
+       'ranch',
+       'random',
+       'range',
+       'rapid',
+       'rare',
+       'rate',
+       'rather',
+       'raven',
+       'raw',
+       'razor',
+       'ready',
+       'real',
+       'reason',
+       'rebel',
+       'rebuild',
+       'recall',
+       'receive',
+       'recipe',
+       'record',
+       'recycle',
+       'reduce',
+       'reflect',
+       'reform',
+       'refuse',
+       'region',
+       'regret',
+       'regular',
+       'reject',
+       'relax',
+       'release',
+       'relief',
+       'rely',
+       'remain',
+       'remember',
+       'remind',
+       'remove',
+       'render',
+       'renew',
+       'rent',
+       'reopen',
+       'repair',
+       'repeat',
+       'replace',
+       'report',
+       'require',
+       'rescue',
+       'resemble',
+       'resist',
+       'resource',
+       'response',
+       'result',
+       'retire',
+       'retreat',
+       'return',
+       'reunion',
+       'reveal',
+       'review',
+       'reward',
+       'rhythm',
+       'rib',
+       'ribbon',
+       'rice',
+       'rich',
+       'ride',
+       'ridge',
+       'rifle',
+       'right',
+       'rigid',
+       'ring',
+       'riot',
+       'ripple',
+       'risk',
+       'ritual',
+       'rival',
+       'river',
+       'road',
+       'roast',
+       'robot',
+       'robust',
+       'rocket',
+       'romance',
+       'roof',
+       'rookie',
+       'room',
+       'rose',
+       'rotate',
+       'rough',
+       'round',
+       'route',
+       'royal',
+       'rubber',
+       'rude',
+       'rug',
+       'rule',
+       'run',
+       'runway',
+       'rural',
+       'sad',
+       'saddle',
+       'sadness',
+       'safe',
+       'sail',
+       'salad',
+       'salmon',
+       'salon',
+       'salt',
+       'salute',
+       'same',
+       'sample',
+       'sand',
+       'satisfy',
+       'satoshi',
+       'sauce',
+       'sausage',
+       'save',
+       'say',
+       'scale',
+       'scan',
+       'scare',
+       'scatter',
+       'scene',
+       'scheme',
+       'school',
+       'science',
+       'scissors',
+       'scorpion',
+       'scout',
+       'scrap',
+       'screen',
+       'script',
+       'scrub',
+       'sea',
+       'search',
+       'season',
+       'seat',
+       'second',
+       'secret',
+       'section',
+       'security',
+       'seed',
+       'seek',
+       'segment',
+       'select',
+       'sell',
+       'seminar',
+       'senior',
+       'sense',
+       'sentence',
+       'series',
+       'service',
+       'session',
+       'settle',
+       'setup',
+       'seven',
+       'shadow',
+       'shaft',
+       'shallow',
+       'share',
+       'shed',
+       'shell',
+       'sheriff',
+       'shield',
+       'shift',
+       'shine',
+       'ship',
+       'shiver',
+       'shock',
+       'shoe',
+       'shoot',
+       'shop',
+       'short',
+       'shoulder',
+       'shove',
+       'shrimp',
+       'shrug',
+       'shuffle',
+       'shy',
+       'sibling',
+       'sick',
+       'side',
+       'siege',
+       'sight',
+       'sign',
+       'silent',
+       'silk',
+       'silly',
+       'silver',
+       'similar',
+       'simple',
+       'since',
+       'sing',
+       'siren',
+       'sister',
+       'situate',
+       'six',
+       'size',
+       'skate',
+       'sketch',
+       'ski',
+       'skill',
+       'skin',
+       'skirt',
+       'skull',
+       'slab',
+       'slam',
+       'sleep',
+       'slender',
+       'slice',
+       'slide',
+       'slight',
+       'slim',
+       'slogan',
+       'slot',
+       'slow',
+       'slush',
+       'small',
+       'smart',
+       'smile',
+       'smoke',
+       'smooth',
+       'snack',
+       'snake',
+       'snap',
+       'sniff',
+       'snow',
+       'soap',
+       'soccer',
+       'social',
+       'sock',
+       'soda',
+       'soft',
+       'solar',
+       'soldier',
+       'solid',
+       'solution',
+       'solve',
+       'someone',
+       'song',
+       'soon',
+       'sorry',
+       'sort',
+       'soul',
+       'sound',
+       'soup',
+       'source',
+       'south',
+       'space',
+       'spare',
+       'spatial',
+       'spawn',
+       'speak',
+       'special',
+       'speed',
+       'spell',
+       'spend',
+       'sphere',
+       'spice',
+       'spider',
+       'spike',
+       'spin',
+       'spirit',
+       'split',
+       'spoil',
+       'sponsor',
+       'spoon',
+       'sport',
+       'spot',
+       'spray',
+       'spread',
+       'spring',
+       'spy',
+       'square',
+       'squeeze',
+       'squirrel',
+       'stable',
+       'stadium',
+       'staff',
+       'stage',
+       'stairs',
+       'stamp',
+       'stand',
+       'start',
+       'state',
+       'stay',
+       'steak',
+       'steel',
+       'stem',
+       'step',
+       'stereo',
+       'stick',
+       'still',
+       'sting',
+       'stock',
+       'stomach',
+       'stone',
+       'stool',
+       'story',
+       'stove',
+       'strategy',
+       'street',
+       'strike',
+       'strong',
+       'struggle',
+       'student',
+       'stuff',
+       'stumble',
+       'style',
+       'subject',
+       'submit',
+       'subway',
+       'success',
+       'such',
+       'sudden',
+       'suffer',
+       'sugar',
+       'suggest',
+       'suit',
+       'summer',
+       'sun',
+       'sunny',
+       'sunset',
+       'super',
+       'supply',
+       'supreme',
+       'sure',
+       'surface',
+       'surge',
+       'surprise',
+       'surround',
+       'survey',
+       'suspect',
+       'sustain',
+       'swallow',
+       'swamp',
+       'swap',
+       'swarm',
+       'swear',
+       'sweet',
+       'swift',
+       'swim',
+       'swing',
+       'switch',
+       'sword',
+       'symbol',
+       'symptom',
+       'syrup',
+       'system',
+       'table',
+       'tackle',
+       'tag',
+       'tail',
+       'talent',
+       'talk',
+       'tank',
+       'tape',
+       'target',
+       'task',
+       'taste',
+       'tattoo',
+       'taxi',
+       'teach',
+       'team',
+       'tell',
+       'ten',
+       'tenant',
+       'tennis',
+       'tent',
+       'term',
+       'test',
+       'text',
+       'thank',
+       'that',
+       'theme',
+       'then',
+       'theory',
+       'there',
+       'they',
+       'thing',
+       'this',
+       'thought',
+       'three',
+       'thrive',
+       'throw',
+       'thumb',
+       'thunder',
+       'ticket',
+       'tide',
+       'tiger',
+       'tilt',
+       'timber',
+       'time',
+       'tiny',
+       'tip',
+       'tired',
+       'tissue',
+       'title',
+       'toast',
+       'tobacco',
+       'today',
+       'toddler',
+       'toe',
+       'together',
+       'toilet',
+       'token',
+       'tomato',
+       'tomorrow',
+       'tone',
+       'tongue',
+       'tonight',
+       'tool',
+       'tooth',
+       'top',
+       'topic',
+       'topple',
+       'torch',
+       'tornado',
+       'tortoise',
+       'toss',
+       'total',
+       'tourist',
+       'toward',
+       'tower',
+       'town',
+       'toy',
+       'track',
+       'trade',
+       'traffic',
+       'tragic',
+       'train',
+       'transfer',
+       'trap',
+       'trash',
+       'travel',
+       'tray',
+       'treat',
+       'tree',
+       'trend',
+       'trial',
+       'tribe',
+       'trick',
+       'trigger',
+       'trim',
+       'trip',
+       'trophy',
+       'trouble',
+       'truck',
+       'true',
+       'truly',
+       'trumpet',
+       'trust',
+       'truth',
+       'try',
+       'tube',
+       'tuition',
+       'tumble',
+       'tuna',
+       'tunnel',
+       'turkey',
+       'turn',
+       'turtle',
+       'twelve',
+       'twenty',
+       'twice',
+       'twin',
+       'twist',
+       'two',
+       'type',
+       'typical',
+       'ugly',
+       'umbrella',
+       'unable',
+       'unaware',
+       'uncle',
+       'uncover',
+       'under',
+       'undo',
+       'unfair',
+       'unfold',
+       'unhappy',
+       'uniform',
+       'unique',
+       'unit',
+       'universe',
+       'unknown',
+       'unlock',
+       'until',
+       'unusual',
+       'unveil',
+       'update',
+       'upgrade',
+       'uphold',
+       'upon',
+       'upper',
+       'upset',
+       'urban',
+       'urge',
+       'usage',
+       'use',
+       'used',
+       'useful',
+       'useless',
+       'usual',
+       'utility',
+       'vacant',
+       'vacuum',
+       'vague',
+       'valid',
+       'valley',
+       'valve',
+       'van',
+       'vanish',
+       'vapor',
+       'various',
+       'vast',
+       'vault',
+       'vehicle',
+       'velvet',
+       'vendor',
+       'venture',
+       'venue',
+       'verb',
+       'verify',
+       'version',
+       'very',
+       'vessel',
+       'veteran',
+       'viable',
+       'vibrant',
+       'vicious',
+       'victory',
+       'video',
+       'view',
+       'village',
+       'vintage',
+       'violin',
+       'virtual',
+       'virus',
+       'visa',
+       'visit',
+       'visual',
+       'vital',
+       'vivid',
+       'vocal',
+       'voice',
+       'void',
+       'volcano',
+       'volume',
+       'vote',
+       'voyage',
+       'wage',
+       'wagon',
+       'wait',
+       'walk',
+       'wall',
+       'walnut',
+       'want',
+       'warfare',
+       'warm',
+       'warrior',
+       'wash',
+       'wasp',
+       'waste',
+       'water',
+       'wave',
+       'way',
+       'wealth',
+       'weapon',
+       'wear',
+       'weasel',
+       'weather',
+       'web',
+       'wedding',
+       'weekend',
+       'weird',
+       'welcome',
+       'west',
+       'wet',
+       'whale',
+       'what',
+       'wheat',
+       'wheel',
+       'when',
+       'where',
+       'whip',
+       'whisper',
+       'wide',
+       'width',
+       'wife',
+       'wild',
+       'will',
+       'win',
+       'window',
+       'wine',
+       'wing',
+       'wink',
+       'winner',
+       'winter',
+       'wire',
+       'wisdom',
+       'wise',
+       'wish',
+       'witness',
+       'wolf',
+       'woman',
+       'wonder',
+       'wood',
+       'wool',
+       'word',
+       'work',
+       'world',
+       'worry',
+       'worth',
+       'wrap',
+       'wreck',
+       'wrestle',
+       'wrist',
+       'write',
+       'wrong',
+       'yard',
+       'year',
+       'yellow',
+       'you',
+       'young',
+       'youth',
+       'zebra',
+       'zero',
+       'zone',
+       'zoo',
+
+]
+
+export default words
diff --git a/package-lock.json b/package-lock.json
new file mode 100644 (file)
index 0000000..7783b0d
--- /dev/null
@@ -0,0 +1,30 @@
+{
+       "name": "nanocurrency-web",
+       "version": "1.0.0",
+       "lockfileVersion": 1,
+       "requires": true,
+       "dependencies": {
+               "@types/node": {
+                       "version": "12.7.12",
+                       "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.12.tgz",
+                       "integrity": "sha512-KPYGmfD0/b1eXurQ59fXD1GBzhSQfz6/lKBxkaHX9dKTzjXbK68Zt7yGUxUsCS1jeTy/8aL+d9JEr+S54mpkWQ==",
+                       "dev": true
+               },
+               "bignumber": {
+                       "version": "1.1.0",
+                       "resolved": "https://registry.npmjs.org/bignumber/-/bignumber-1.1.0.tgz",
+                       "integrity": "sha1-5qsKdD2l8+oBjlwXWX0SH3howVk="
+               },
+               "blakejs": {
+                       "version": "1.1.0",
+                       "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.1.0.tgz",
+                       "integrity": "sha1-ad+S75U6qIylGjLfarHFShVfx6U="
+               },
+               "typescript": {
+                       "version": "3.6.3",
+                       "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.3.tgz",
+                       "integrity": "sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw==",
+                       "dev": true
+               }
+       }
+}
diff --git a/package.json b/package.json
new file mode 100644 (file)
index 0000000..4bf0015
--- /dev/null
@@ -0,0 +1,27 @@
+{
+       "name": "nanocurrency-web",
+       "version": "1.0.0",
+       "description": "Toolset for Nano cryptocurrency client side offline integrations",
+       "author": "Miro Metsänheimo <miro@metsanheimo.fi>",
+       "license": "MIT",
+       "homepage": "https://github.com/numsu/nanocurrency-web-js#readme",
+       "repository": {
+               "type": "git",
+               "url": "git+https://github.com/numsu/nanocurrency-web-js.git"
+       },
+       "bugs": {
+               "url": "https://github.com/numsu/nanocurrency-web-js/issues"
+       },
+       "main": "index.js",
+       "scripts": {
+               "test": "echo \"Error: no test specified\" && exit 1"
+       },
+       "dependencies": {
+               "bignumber": "^1.1.0",
+               "blakejs": "^1.1.0"
+       },
+       "devDependencies": {
+               "@types/node": "^12.7.12",
+               "typescript": "3.6.3"
+       }
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644 (file)
index 0000000..e3007c0
--- /dev/null
@@ -0,0 +1,18 @@
+{
+       "compilerOptions": {
+               "target": "es5",
+               "module": "commonjs",
+               "declaration": true,
+               "outDir": "./dist",
+               "strict": true,
+               "esModuleInterop": true,
+               "downlevelIteration": true,
+               "types": [
+                       "node",
+                       "index.d.ts"
+               ],
+               "lib": [
+                       "es2017"
+               ]
+       }
+}
\ No newline at end of file