--- /dev/null
+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,
+}
--- /dev/null
+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,
+ }],
+ }
+ }
+
+}
--- /dev/null
+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,
+ }
+ }
+
+}
--- /dev/null
+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,
+ }
+ }
+
+}
--- /dev/null
+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)
+ }
+
+}
--- /dev/null
+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
+}
--- /dev/null
+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)
+ // }
+
+}
--- /dev/null
+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
+ }
+
+}
--- /dev/null
+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`
+ }
+ }
+
+}
--- /dev/null
+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)
+ }
+
+}
--- /dev/null
+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,
+ }
+ }
+
+}
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+{
+ "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
+ }
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+}
--- /dev/null
+{
+ "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