-
Notifications
You must be signed in to change notification settings - Fork 14
/
index.ts
398 lines (363 loc) · 13.3 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
import BigNumber from 'bignumber.js'
import AddressGenerator from './lib/address-generator'
import AddressImporter, { Account, Wallet } from './lib/address-importer'
import BlockSigner, { BlockData, ReceiveBlock, RepresentativeBlock, SendBlock, SignedBlock } from './lib/block-signer'
import Box from './lib/box'
import NanoAddress from './lib/nano-address'
import NanoConverter from './lib/nano-converter'
import Signer from './lib/signer'
import Convert from './lib/util/convert'
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 by default. 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 wallet without the
* password is not possible.
*
* @param {string} [entropy] - (Optional) 64 byte hexadecimal string entropy to be used instead of generating it
* @param {string} [seedPassword] - (Optional) seed password
* @returns {Wallet} The wallet
*/
generate: (entropy?: string, seedPassword?: string): Wallet => {
return AddressGenerator.generateWallet(entropy, seedPassword)
},
/**
* Generate a new Nano cryptocurrency wallet
*
* This function generates a legacy wallet from a random seed. Wallet includes
* a mnemonic phrase and a seed, the account is derived from the seed at 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 seed by default. You can give your own seed
* as a parameter and it will be used instead.
*
* @param {string} [seed] - (Optional) 64 byte hexadecimal string seed to be used instead of generating it
* @returns {Wallet} The wallet
*/
generateLegacy: (seed?: string): Wallet => {
return AddressGenerator.generateLegacyWallet(seed)
},
/**
* 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 {Wallet} The wallet
*/
fromMnemonic: (mnemonic: string, seedPassword?: string): Wallet => {
return AddressImporter.fromMnemonic(mnemonic, seedPassword)
},
/**
* Import a Nano cryptocurrency wallet from a legacy mnemonic phrase
*
* This function imports a wallet from an old Nano mnemonic phrase. Wallet includes the mnemonic
* phrase, a seed which represents the mnemonic and an account derived from the seed at 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
* @throws Throws an error if the mnemonic phrase doesn't pass validations
* @returns {Wallet} The wallet
*/
fromLegacyMnemonic: (mnemonic: string): Wallet => {
return AddressImporter.fromLegacyMnemonic(mnemonic)
},
/**
* 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 {Wallet} The wallet, without the mnemonic phrase because it's not possible to infer backwards
*/
fromSeed: (seed: string): Wallet => {
return AddressImporter.fromSeed(seed)
},
/**
* Import Nano cryptocurrency accounts from a legacy hex seed
*
* This function imports a wallet from a seed. The private key is derived from the seed using
* simply a blake2b hash function. The public key is derived from the private key using the ed25519 curve
* algorithm.
*
* The Nano address is derived from the public key using standard Nano encoding.
* The address is prefixed with 'nano_'.
*
* @param {string} seed - The seed
* @returns {Wallet} The wallet
*/
fromLegacySeed: (seed: string): Wallet => {
return AddressImporter.fromLegacySeed(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
* @returns {Account[]} a list of accounts
*/
accounts: (seed: string, from: number, to: number): Account[] => {
return AddressImporter.fromSeed(seed, from, to).accounts
},
/**
* Derive accounts for the legacy hex seed
*
* This function derives Nano accounts with the given seed with 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
* @returns {Account[]} a list of accounts
*/
legacyAccounts: (seed: string, from: number, to: number): Account[] => {
return AddressImporter.fromLegacySeed(seed, from, to).accounts
},
}
const block = {
/**
* Sign a send block with the input parameters
*
* For a send block, put your own address to the 'fromAddress' property and
* the recipient address to the 'toAddress' property.
* All the NANO amounts should be input in RAW format. The addresses should be
* valid Nano addresses. Fetch the current balance, frontier (previous block) and
* representative address from the network.
*
* The return value of this function is ready to be published to the network.
*
* NOTICE: Always fetch up-to-date account info from the network
* before signing the block.
*
* @param {SendBlock} data The data for the block
* @param {string} privateKey Private key to sign the block
* @returns {SignedBlock} the signed block
*/
send: (data: SendBlock, privateKey: string): SignedBlock => {
return BlockSigner.send(data, privateKey)
},
/**
* Sign a receive block with the input parameters
*
* For a receive block, put your own address to the 'toAddress' property.
* All the NANO amounts should be input in RAW format. The addresses should be
* valid Nano addresses. Fetch the current balance, frontier (previous block) and
* representative address from the network.
* Input the receive amount and transaction hash from the pending block.
*
* The return value of this function is ready to be published to the network.
*
* NOTICE: Always fetch up-to-date account info from the network
* before signing the block.
*
* @param {ReceiveBlock} data The data for the block
* @param {string} privateKey Private key to sign the block
* @returns {SignedBlock} the signed block
*/
receive: (data: ReceiveBlock, privateKey: string): SignedBlock => {
return BlockSigner.receive(data, privateKey)
},
/**
* Sign a representative change block with the input parameters
*
* For a change block, put your own address to the 'address' property.
* All the NANO amounts should be input in RAW format. The addresses should be
* valid Nano addresses. Fetch the current balance, previous block from the
* network. Set the new representative address
* as the representative.
*
* NOTICE: Always fetch up-to-date account info from the network
* before signing the block.
*
* @param {RepresentativeBlock} data The data for the block
* @param {string} privateKey Private key to sign the block
* @returns {SignedBlock} the signed block
*/
representative: (data: RepresentativeBlock, privateKey: string): SignedBlock => {
const block: SendBlock = {
...data,
fromAddress: data.address,
amountRaw: '0',
toAddress: 'nano_1111111111111111111111111111111111111111111111111111hifc8npp', // Burn address
}
return BlockSigner.send(block, privateKey)
},
}
const tools = {
/**
* Convert Nano values
*
* Possible units are RAW, NANO, MRAI, KRAI, RAI
*
* @param {string | BigNumber} input The input value
* @param {string} inputUnit The unit of the input value
* @param {string} outputUnit The unit you wish to convert to
* @returns {string} The converted value
*/
convert: (input: string | BigNumber, inputUnit: string, outputUnit: string): string => {
return NanoConverter.convert(input, inputUnit, outputUnit)
},
/**
* Sign any strings with the user's private key
*
* @param {string} privateKey The private key to sign with
* @param {...string} input Data to sign
* @returns {string} The signature
*/
sign: (privateKey: string, ...input: string[]): string => {
const data = input.map(Convert.stringToHex)
return Signer.sign(privateKey, ...data)
},
/**
* Verifies the signature of any input string
*
* @param {string} publicKey The public key to verify with
* @param {string} signature The signature to verify
* @param {...string} input Data to verify
* @returns {boolean} valid or not
*/
verify: (publicKey: string, signature: string, ...input: string[]): boolean => {
const data = input.map(Convert.stringToHex)
return Signer.verify(publicKey, signature, ...data)
},
/**
* Verifies the signature of any input string
*
* @param {string} publicKey The public key to verify with
* @param {BlockData} block The block to verify
* @returns {boolean} valid or not
*/
verifyBlock: (publicKey: string, block: BlockData): boolean => {
const preamble = 0x6.toString().padStart(64, '0')
return Signer.verify(publicKey, block.signature,
preamble,
NanoAddress.nanoAddressToHexString(block.account),
block.previous,
NanoAddress.nanoAddressToHexString(block.representative),
Convert.dec2hex(block.balance, 16).toUpperCase(),
block.link)
},
/**
* Validate a Nano address
*
* @param {string} input The address to validate
* @returns {boolean} valid or not
*/
validateAddress: (input: string): boolean => {
return NanoAddress.validateNanoAddress(input)
},
/**
* Validate mnemonic words
*
* @param {string} input The address to validate
* @returns {boolean} valid or not
*/
validateMnemonic: (input: string): boolean => {
return AddressImporter.validateMnemonic(input)
},
/**
* Convert a Nano address to a public key
*
* @param {string} input Nano address to convert
* @returns {string} the public key
*/
addressToPublicKey: (input: string): string => {
return NanoAddress.addressToPublicKey(input)
},
/**
* Convert a public key to a Nano address
*
* @param {string} input Public key to convert
* @returns {string} the nano address
*/
publicKeyToAddress: (input: string): string => {
return NanoAddress.deriveAddress(input)
},
/**
* Hash a string or array of strings with blake2b
*
* @param {string | string[]} input string to hash
* @returns {string} hashed string
*/
blake2b: (input: string | string[]): string => {
if (Array.isArray(input)) {
return Convert.ab2hex(Signer.generateHash(input.map(Convert.stringToHex)))
} else {
return Convert.ab2hex(Signer.generateHash([Convert.stringToHex(input)]))
}
},
}
const box = {
/**
* Encrypt a message using a Nano address private key for
* end-to-end encrypted messaging.
*
* Encrypts the message using the recipient's public key,
* the sender's private key and a random nonce generated
* inside the library. The message can be opened with the
* recipient's private key and the sender's public key by
* using the decrypt method.
*
* @param {string} message string to encrypt
* @param {string} address nano address of the recipient
* @param {string} privateKey private key of the sender
* @returns {string} encrypted message encoded in Base64
*/
encrypt: (message: string, address: string, privateKey: string): string => {
return Box.encrypt(message, address, privateKey)
},
/**
* Decrypt a message using a Nano address private key.
*
* Decrypts the message by using the sender's public key,
* the recipient's private key and the nonce which is included
* in the encrypted message.
*
* @param {string} encrypted string to decrypt
* @param {string} address nano address of the sender
* @param {string} privateKey private key of the recipient
* @returns {string} decrypted message encoded in UTF-8
*/
decrypt: (encrypted: string, address: string, privateKey: string): string => {
return Box.decrypt(encrypted, address, privateKey)
}
}
export {
wallet,
block,
tools,
box,
}