@neuraiproject/neurai-key 2.8.3 → 2.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.vscode/settings.json +3 -0
- package/README.md +97 -21
- package/dist/NeuraiKey.js +173 -157
- package/dist/main.js +32 -16
- package/dist/main.js.map +1 -1
- package/dist/module.js +33 -14
- package/dist/module.js.map +1 -1
- package/dist/types.d.ts +8 -4
- package/dist/types.d.ts.map +1 -1
- package/example-passphrase.js +34 -0
- package/index.ts +43 -15
- package/package.json +3 -2
- package/test-html/NeuraiKey.js +60497 -0
- package/test-html/index.html +543 -0
- package/test.js +65 -20
- package/types.ts +1 -0
|
@@ -0,0 +1,543 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Neurai-Key Verifier</title>
|
|
7
|
+
<style>
|
|
8
|
+
* {
|
|
9
|
+
margin: 0;
|
|
10
|
+
padding: 0;
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
body {
|
|
15
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
16
|
+
background: #f5f5f5;
|
|
17
|
+
min-height: 100vh;
|
|
18
|
+
padding: 20px;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.container {
|
|
22
|
+
max-width: 900px;
|
|
23
|
+
margin: 0 auto;
|
|
24
|
+
background: white;
|
|
25
|
+
border-radius: 8px;
|
|
26
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
27
|
+
padding: 40px;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
h1 {
|
|
31
|
+
color: #2c3e50;
|
|
32
|
+
text-align: center;
|
|
33
|
+
margin-bottom: 8px;
|
|
34
|
+
font-size: 2em;
|
|
35
|
+
font-weight: 600;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.subtitle {
|
|
39
|
+
text-align: center;
|
|
40
|
+
color: #7f8c8d;
|
|
41
|
+
margin-bottom: 30px;
|
|
42
|
+
font-size: 0.95em;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.input-section {
|
|
46
|
+
background: #fafafa;
|
|
47
|
+
padding: 20px;
|
|
48
|
+
border-radius: 6px;
|
|
49
|
+
margin-bottom: 25px;
|
|
50
|
+
border: 1px solid #e0e0e0;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
label {
|
|
54
|
+
display: block;
|
|
55
|
+
color: #2c3e50;
|
|
56
|
+
font-weight: 500;
|
|
57
|
+
margin-bottom: 8px;
|
|
58
|
+
font-size: 0.95em;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
textarea {
|
|
62
|
+
width: 100%;
|
|
63
|
+
padding: 12px;
|
|
64
|
+
border: 1px solid #d0d0d0;
|
|
65
|
+
border-radius: 4px;
|
|
66
|
+
font-size: 14px;
|
|
67
|
+
font-family: 'Courier New', monospace;
|
|
68
|
+
resize: vertical;
|
|
69
|
+
min-height: 100px;
|
|
70
|
+
transition: border-color 0.2s;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
textarea:focus {
|
|
74
|
+
outline: none;
|
|
75
|
+
border-color: #3498db;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.network-selector {
|
|
79
|
+
margin: 15px 0;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
select {
|
|
83
|
+
width: 100%;
|
|
84
|
+
padding: 10px 12px;
|
|
85
|
+
border: 1px solid #d0d0d0;
|
|
86
|
+
border-radius: 4px;
|
|
87
|
+
font-size: 14px;
|
|
88
|
+
background: white;
|
|
89
|
+
cursor: pointer;
|
|
90
|
+
transition: border-color 0.2s;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
select:focus {
|
|
94
|
+
outline: none;
|
|
95
|
+
border-color: #3498db;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.button-group {
|
|
99
|
+
display: flex;
|
|
100
|
+
gap: 10px;
|
|
101
|
+
margin-top: 20px;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
button {
|
|
105
|
+
flex: 1;
|
|
106
|
+
padding: 12px 24px;
|
|
107
|
+
font-size: 14px;
|
|
108
|
+
font-weight: 500;
|
|
109
|
+
border: none;
|
|
110
|
+
border-radius: 4px;
|
|
111
|
+
cursor: pointer;
|
|
112
|
+
transition: all 0.2s;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.btn-primary {
|
|
116
|
+
background: #3498db;
|
|
117
|
+
color: white;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.btn-primary:hover {
|
|
121
|
+
background: #2980b9;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.btn-secondary {
|
|
125
|
+
background: #95a5a6;
|
|
126
|
+
color: white;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.btn-secondary:hover {
|
|
130
|
+
background: #7f8c8d;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.btn-generate {
|
|
134
|
+
background: #27ae60;
|
|
135
|
+
color: white;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.btn-generate:hover {
|
|
139
|
+
background: #229954;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.results {
|
|
143
|
+
margin-top: 30px;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.address-card {
|
|
147
|
+
background: #fafafa;
|
|
148
|
+
padding: 15px;
|
|
149
|
+
border-radius: 4px;
|
|
150
|
+
margin-bottom: 15px;
|
|
151
|
+
border-left: 3px solid #3498db;
|
|
152
|
+
border: 1px solid #e0e0e0;
|
|
153
|
+
border-left: 3px solid #3498db;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.address-card:hover {
|
|
157
|
+
background: #f5f5f5;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.address-header {
|
|
161
|
+
font-size: 1em;
|
|
162
|
+
font-weight: 600;
|
|
163
|
+
color: #2c3e50;
|
|
164
|
+
margin-bottom: 12px;
|
|
165
|
+
display: flex;
|
|
166
|
+
justify-content: space-between;
|
|
167
|
+
align-items: center;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.address-type {
|
|
171
|
+
font-size: 0.75em;
|
|
172
|
+
background: #3498db;
|
|
173
|
+
color: white;
|
|
174
|
+
padding: 4px 8px;
|
|
175
|
+
border-radius: 3px;
|
|
176
|
+
font-weight: 500;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.address-info {
|
|
180
|
+
margin: 10px 0;
|
|
181
|
+
display: flex;
|
|
182
|
+
align-items: center;
|
|
183
|
+
gap: 10px;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.info-label {
|
|
187
|
+
font-weight: 600;
|
|
188
|
+
color: #555;
|
|
189
|
+
display: inline-block;
|
|
190
|
+
width: 120px;
|
|
191
|
+
font-size: 0.9em;
|
|
192
|
+
flex-shrink: 0;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.info-value {
|
|
196
|
+
font-family: 'Courier New', monospace;
|
|
197
|
+
color: #2c3e50;
|
|
198
|
+
word-break: break-all;
|
|
199
|
+
background: white;
|
|
200
|
+
padding: 8px 10px;
|
|
201
|
+
border-radius: 3px;
|
|
202
|
+
font-size: 0.85em;
|
|
203
|
+
border: 1px solid #e0e0e0;
|
|
204
|
+
flex: 1 1 auto;
|
|
205
|
+
min-width: 0;
|
|
206
|
+
overflow: hidden;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.error {
|
|
210
|
+
background: #fee;
|
|
211
|
+
color: #c53030;
|
|
212
|
+
padding: 12px;
|
|
213
|
+
border-radius: 4px;
|
|
214
|
+
margin: 15px 0;
|
|
215
|
+
border-left: 3px solid #fc8181;
|
|
216
|
+
font-size: 0.9em;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.success {
|
|
220
|
+
background: #f0fff4;
|
|
221
|
+
color: #22543d;
|
|
222
|
+
padding: 12px;
|
|
223
|
+
border-radius: 4px;
|
|
224
|
+
margin: 15px 0;
|
|
225
|
+
border-left: 3px solid #68d391;
|
|
226
|
+
font-size: 0.9em;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.info {
|
|
230
|
+
background: #e6f7ff;
|
|
231
|
+
color: #003a70;
|
|
232
|
+
padding: 12px;
|
|
233
|
+
border-radius: 4px;
|
|
234
|
+
margin: 15px 0;
|
|
235
|
+
border-left: 3px solid #69c0ff;
|
|
236
|
+
font-size: 0.9em;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.loading {
|
|
240
|
+
text-align: center;
|
|
241
|
+
padding: 30px;
|
|
242
|
+
color: #7f8c8d;
|
|
243
|
+
font-size: 1em;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.hidden {
|
|
247
|
+
display: none;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.copy-btn {
|
|
251
|
+
background: transparent;
|
|
252
|
+
color: #555;
|
|
253
|
+
border: none;
|
|
254
|
+
padding: 6px 8px;
|
|
255
|
+
border-radius: 3px;
|
|
256
|
+
cursor: pointer;
|
|
257
|
+
font-size: 1.2em;
|
|
258
|
+
transition: all 0.2s;
|
|
259
|
+
flex: 0 0 auto;
|
|
260
|
+
width: auto;
|
|
261
|
+
line-height: 1;
|
|
262
|
+
display: inline-flex;
|
|
263
|
+
align-items: center;
|
|
264
|
+
justify-content: center;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
.copy-btn:hover {
|
|
268
|
+
color: #27ae60;
|
|
269
|
+
transform: scale(1.1);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
@media (max-width: 768px) {
|
|
273
|
+
.container {
|
|
274
|
+
padding: 20px;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
h1 {
|
|
278
|
+
font-size: 1.8em;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.button-group {
|
|
282
|
+
flex-direction: column;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.info-label {
|
|
286
|
+
display: block;
|
|
287
|
+
margin-bottom: 5px;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.info-value {
|
|
291
|
+
display: block;
|
|
292
|
+
max-width: 100%;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
</style>
|
|
296
|
+
</head>
|
|
297
|
+
<body>
|
|
298
|
+
<div class="container">
|
|
299
|
+
<h1>Neurai Key Verifier</h1>
|
|
300
|
+
<p class="subtitle">Generate and verify addresses from your mnemonic (12 or 24 words)</p>
|
|
301
|
+
|
|
302
|
+
<div class="input-section">
|
|
303
|
+
<label for="mnemonic">Mnemonic Phrase (12 or 24 words):</label>
|
|
304
|
+
<textarea
|
|
305
|
+
id="mnemonic"
|
|
306
|
+
placeholder="Enter your mnemonic phrase here, separated by spaces. Example: word1 word2 word3..."
|
|
307
|
+
></textarea>
|
|
308
|
+
|
|
309
|
+
<div class="network-selector">
|
|
310
|
+
<label for="network">Network:</label>
|
|
311
|
+
<select id="network">
|
|
312
|
+
<option value="xna">Neurai Mainnet (XNA)</option>
|
|
313
|
+
<option value="xna-test">Neurai Testnet (XNA-TEST)</option>
|
|
314
|
+
</select>
|
|
315
|
+
</div>
|
|
316
|
+
|
|
317
|
+
<div class="network-selector">
|
|
318
|
+
<label for="passphrase">Passphrase (Optional - BIP39 25th word):</label>
|
|
319
|
+
<input
|
|
320
|
+
type="text"
|
|
321
|
+
id="passphrase"
|
|
322
|
+
placeholder="Enter optional passphrase for extra security"
|
|
323
|
+
style="width: 100%; padding: 12px; border: 1px solid #d0d0d0; border-radius: 4px; font-size: 14px;"
|
|
324
|
+
/>
|
|
325
|
+
<small style="color: #7f8c8d; font-size: 0.85em; display: block; margin-top: 5px;">
|
|
326
|
+
⚠️ Warning: Different passphrases create different wallets. If you use a passphrase, you'll need both the mnemonic AND passphrase to recover your wallet.
|
|
327
|
+
</small>
|
|
328
|
+
</div>
|
|
329
|
+
|
|
330
|
+
<div class="button-group">
|
|
331
|
+
<button class="btn-primary" onclick="verificarYGenerar()">
|
|
332
|
+
Verify and Generate Addresses
|
|
333
|
+
</button>
|
|
334
|
+
<button class="btn-generate" onclick="generarNuevoMnemonic()">
|
|
335
|
+
Generate New Mnemonic
|
|
336
|
+
</button>
|
|
337
|
+
<button class="btn-secondary" onclick="limpiar()">
|
|
338
|
+
Clear
|
|
339
|
+
</button>
|
|
340
|
+
</div>
|
|
341
|
+
</div>
|
|
342
|
+
|
|
343
|
+
<div id="message"></div>
|
|
344
|
+
<div id="results" class="results"></div>
|
|
345
|
+
</div>
|
|
346
|
+
|
|
347
|
+
<script src="./NeuraiKey.js"></script>
|
|
348
|
+
<script>
|
|
349
|
+
function mostrarMensaje(mensaje, tipo) {
|
|
350
|
+
const messageDiv = document.getElementById('message');
|
|
351
|
+
messageDiv.className = tipo;
|
|
352
|
+
messageDiv.innerHTML = mensaje;
|
|
353
|
+
messageDiv.classList.remove('hidden');
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function ocultarMensaje() {
|
|
357
|
+
const messageDiv = document.getElementById('message');
|
|
358
|
+
messageDiv.classList.add('hidden');
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function limpiar() {
|
|
362
|
+
document.getElementById('mnemonic').value = '';
|
|
363
|
+
document.getElementById('passphrase').value = '';
|
|
364
|
+
document.getElementById('results').innerHTML = '';
|
|
365
|
+
ocultarMensaje();
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function generarNuevoMnemonic() {
|
|
369
|
+
try {
|
|
370
|
+
const mnemonic = NeuraiKey.generateMnemonic();
|
|
371
|
+
document.getElementById('mnemonic').value = mnemonic;
|
|
372
|
+
mostrarMensaje('New mnemonic generated. Save it in a safe place!', 'success');
|
|
373
|
+
document.getElementById('results').innerHTML = '';
|
|
374
|
+
} catch (error) {
|
|
375
|
+
mostrarMensaje(`Error generating mnemonic: ${error.message}`, 'error');
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function copiarTexto(texto, boton) {
|
|
380
|
+
navigator.clipboard.writeText(texto).then(() => {
|
|
381
|
+
const textoOriginal = boton.textContent;
|
|
382
|
+
boton.textContent = '✓';
|
|
383
|
+
setTimeout(() => {
|
|
384
|
+
boton.textContent = textoOriginal;
|
|
385
|
+
}, 2000);
|
|
386
|
+
}).catch(err => {
|
|
387
|
+
alert('Error copying: ' + err);
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function verificarYGenerar() {
|
|
392
|
+
ocultarMensaje();
|
|
393
|
+
const resultsDiv = document.getElementById('results');
|
|
394
|
+
resultsDiv.innerHTML = '<div class="loading">Generating addresses...</div>';
|
|
395
|
+
|
|
396
|
+
setTimeout(() => {
|
|
397
|
+
try {
|
|
398
|
+
const mnemonic = document.getElementById('mnemonic').value.trim();
|
|
399
|
+
const network = document.getElementById('network').value;
|
|
400
|
+
const passphrase = document.getElementById('passphrase').value; // Obtener passphrase
|
|
401
|
+
|
|
402
|
+
if (!mnemonic) {
|
|
403
|
+
mostrarMensaje('Please enter a mnemonic phrase', 'error');
|
|
404
|
+
resultsDiv.innerHTML = '';
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Validar que sea un mnemonic válido
|
|
409
|
+
const palabras = mnemonic.split(/\s+/).filter(p => p.length > 0);
|
|
410
|
+
if (palabras.length !== 12 && palabras.length !== 24) {
|
|
411
|
+
mostrarMensaje('Mnemonic must have exactly 12 or 24 words. Words found: ' + palabras.length, 'error');
|
|
412
|
+
resultsDiv.innerHTML = '';
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// Validar el mnemonic con bip39
|
|
417
|
+
if (!NeuraiKey.validateMnemonic || typeof NeuraiKey.validateMnemonic !== 'function') {
|
|
418
|
+
// Si no existe validateMnemonic, intentar generar una dirección para validar
|
|
419
|
+
try {
|
|
420
|
+
NeuraiKey.getAddressPair(network, mnemonic, 0, 0, passphrase);
|
|
421
|
+
} catch (e) {
|
|
422
|
+
mostrarMensaje('Invalid mnemonic: ' + e.message, 'error');
|
|
423
|
+
resultsDiv.innerHTML = '';
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
const passphraseMsg = passphrase ? ' with passphrase' : '';
|
|
429
|
+
mostrarMensaje(`Valid mnemonic (${palabras.length} words). Generating addresses on ${network}${passphraseMsg}...`, 'success');
|
|
430
|
+
|
|
431
|
+
let html = '';
|
|
432
|
+
|
|
433
|
+
// Generar las primeras 5 direcciones
|
|
434
|
+
for (let i = 0; i < 5; i++) {
|
|
435
|
+
const addressPair = NeuraiKey.getAddressPair(network, mnemonic, 0, i, passphrase); // Usar passphrase
|
|
436
|
+
|
|
437
|
+
// Dirección Externa (para recibir)
|
|
438
|
+
html += `
|
|
439
|
+
<div class="address-card">
|
|
440
|
+
<div class="address-header">
|
|
441
|
+
<span>Address #${i + 1} - External (Receive)</span>
|
|
442
|
+
<span class="address-type">EXTERNAL</span>
|
|
443
|
+
</div>
|
|
444
|
+
<div class="address-info">
|
|
445
|
+
<span class="info-label">Address:</span>
|
|
446
|
+
<span class="info-value">${addressPair.external.address}</span>
|
|
447
|
+
<button class="copy-btn" onclick="copiarTexto('${addressPair.external.address}', this)">📋</button>
|
|
448
|
+
</div>
|
|
449
|
+
<div class="address-info">
|
|
450
|
+
<span class="info-label">Path:</span>
|
|
451
|
+
<span class="info-value">${addressPair.external.path}</span>
|
|
452
|
+
</div>
|
|
453
|
+
<div class="address-info">
|
|
454
|
+
<span class="info-label">Public Key:</span>
|
|
455
|
+
<span class="info-value">${addressPair.external.publicKey}</span>
|
|
456
|
+
<button class="copy-btn" onclick="copiarTexto('${addressPair.external.publicKey}', this)">📋</button>
|
|
457
|
+
</div>
|
|
458
|
+
<div class="address-info">
|
|
459
|
+
<span class="info-label">Private Key:</span>
|
|
460
|
+
<span class="info-value">${addressPair.external.privateKey}</span>
|
|
461
|
+
<button class="copy-btn" onclick="copiarTexto('${addressPair.external.privateKey}', this)">📋</button>
|
|
462
|
+
</div>
|
|
463
|
+
<div class="address-info">
|
|
464
|
+
<span class="info-label">WIF:</span>
|
|
465
|
+
<span class="info-value">${addressPair.external.WIF}</span>
|
|
466
|
+
<button class="copy-btn" onclick="copiarTexto('${addressPair.external.WIF}', this)">📋</button>
|
|
467
|
+
</div>
|
|
468
|
+
</div>
|
|
469
|
+
|
|
470
|
+
<div class="address-card">
|
|
471
|
+
<div class="address-header">
|
|
472
|
+
<span>Address #${i + 1} - Internal (Change)</span>
|
|
473
|
+
<span class="address-type" style="background: #f5576c;">INTERNAL</span>
|
|
474
|
+
</div>
|
|
475
|
+
<div class="address-info">
|
|
476
|
+
<span class="info-label">Address:</span>
|
|
477
|
+
<span class="info-value">${addressPair.internal.address}</span>
|
|
478
|
+
<button class="copy-btn" onclick="copiarTexto('${addressPair.internal.address}', this)">📋</button>
|
|
479
|
+
</div>
|
|
480
|
+
<div class="address-info">
|
|
481
|
+
<span class="info-label">Path:</span>
|
|
482
|
+
<span class="info-value">${addressPair.internal.path}</span>
|
|
483
|
+
</div>
|
|
484
|
+
<div class="address-info">
|
|
485
|
+
<span class="info-label">Public Key:</span>
|
|
486
|
+
<span class="info-value">${addressPair.internal.publicKey}</span>
|
|
487
|
+
<button class="copy-btn" onclick="copiarTexto('${addressPair.internal.publicKey}', this)">📋</button>
|
|
488
|
+
</div>
|
|
489
|
+
<div class="address-info">
|
|
490
|
+
<span class="info-label">Private Key:</span>
|
|
491
|
+
<span class="info-value">${addressPair.internal.privateKey}</span>
|
|
492
|
+
<button class="copy-btn" onclick="copiarTexto('${addressPair.internal.privateKey}', this)">📋</button>
|
|
493
|
+
</div>
|
|
494
|
+
<div class="address-info">
|
|
495
|
+
<span class="info-label">WIF:</span>
|
|
496
|
+
<span class="info-value">${addressPair.internal.WIF}</span>
|
|
497
|
+
<button class="copy-btn" onclick="copiarTexto('${addressPair.internal.WIF}', this)">📋</button>
|
|
498
|
+
</div>
|
|
499
|
+
</div>
|
|
500
|
+
`;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
resultsDiv.innerHTML = html;
|
|
504
|
+
|
|
505
|
+
// Agregar mensaje informativo
|
|
506
|
+
const infoDiv = document.createElement('div');
|
|
507
|
+
infoDiv.className = 'info';
|
|
508
|
+
const passphraseInfo = passphrase ? '<br>- 🔐 <strong>Passphrase used:</strong> Remember you need BOTH mnemonic and passphrase to recover these addresses!' : '';
|
|
509
|
+
infoDiv.innerHTML = `
|
|
510
|
+
<strong>Information:</strong><br>
|
|
511
|
+
- The first 5 addresses (external and internal) have been generated<br>
|
|
512
|
+
- <strong>External</strong> addresses are used to receive payments<br>
|
|
513
|
+
- <strong>Internal</strong> addresses are used as change addresses<br>
|
|
514
|
+
- Each pair follows the BIP44 standard with path m/44'/175'/0'/change/index<br>
|
|
515
|
+
- Public keys can be converted back into addresses with <code>NeuraiKey.publicKeyToAddress</code>${passphraseInfo}
|
|
516
|
+
`;
|
|
517
|
+
resultsDiv.appendChild(infoDiv);
|
|
518
|
+
|
|
519
|
+
} catch (error) {
|
|
520
|
+
mostrarMensaje(`Error: ${error.message}`, 'error');
|
|
521
|
+
resultsDiv.innerHTML = '';
|
|
522
|
+
}
|
|
523
|
+
}, 100);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// Permitir verificar con Enter
|
|
527
|
+
document.getElementById('mnemonic').addEventListener('keypress', function(e) {
|
|
528
|
+
if (e.key === 'Enter' && !e.shiftKey) {
|
|
529
|
+
e.preventDefault();
|
|
530
|
+
verificarYGenerar();
|
|
531
|
+
}
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
// También permitir verificar con Enter en el campo de passphrase
|
|
535
|
+
document.getElementById('passphrase').addEventListener('keypress', function(e) {
|
|
536
|
+
if (e.key === 'Enter') {
|
|
537
|
+
e.preventDefault();
|
|
538
|
+
verificarYGenerar();
|
|
539
|
+
}
|
|
540
|
+
});
|
|
541
|
+
</script>
|
|
542
|
+
</body>
|
|
543
|
+
</html>
|
package/test.js
CHANGED
|
@@ -7,49 +7,94 @@ test("Random mnemonic should contain 12 words", () => {
|
|
|
7
7
|
|
|
8
8
|
test("Validate address on main-net", () => {
|
|
9
9
|
const network = "xna";
|
|
10
|
-
const mnemonic =
|
|
11
|
-
"orphan resemble brain dwarf bus fancy horn among cricket logic duty crater";
|
|
10
|
+
const mnemonic = "result pact model attract result puzzle final boss private educate luggage era";
|
|
12
11
|
const address = NeuraiKey.getAddressPair(network, mnemonic, 0, 1);
|
|
13
|
-
expect(address.external.address).toBe("
|
|
12
|
+
expect(address.external.address).toBe("NLdcSXGQvCVf2RTKhx7GZom34f1JADhBTp");
|
|
14
13
|
});
|
|
15
14
|
|
|
16
15
|
test("Validate address on test-net", () => {
|
|
17
16
|
const network = "xna-test";
|
|
18
|
-
const mnemonic =
|
|
19
|
-
"orphan resemble brain dwarf bus fancy horn among cricket logic duty crater";
|
|
17
|
+
const mnemonic = "result pact model attract result puzzle final boss private educate luggage era";
|
|
20
18
|
const address = NeuraiKey.getAddressPair(network, mnemonic, 0, 1);
|
|
21
|
-
expect(address.external.address).toBe("
|
|
19
|
+
expect(address.external.address).toBe("tPXGaMRNwZuV1UKSrD9gABPscrJWUmedQ9");
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("Validate address with passphrase on main-net", () => {
|
|
23
|
+
const network = "xna";
|
|
24
|
+
const mnemonic = "result pact model attract result puzzle final boss private educate luggage era";
|
|
25
|
+
const passphrase = "my secret passphrase";
|
|
26
|
+
const address = NeuraiKey.getAddressPair(network, mnemonic, 0, 1, passphrase);
|
|
27
|
+
// With passphrase, the address should be different from the one without passphrase
|
|
28
|
+
expect(address.external.address).not.toBe("NLdcSXGQvCVf2RTKhx7GZom34f1JADhBTp");
|
|
29
|
+
// Verify it generates consistently with the same passphrase
|
|
30
|
+
const address2 = NeuraiKey.getAddressPair(network, mnemonic, 0, 1, passphrase);
|
|
31
|
+
expect(address.external.address).toBe(address2.external.address);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("Different passphrases generate different addresses", () => {
|
|
35
|
+
const network = "xna";
|
|
36
|
+
const mnemonic = "result pact model attract result puzzle final boss private educate luggage era";
|
|
37
|
+
const passphrase1 = "passphrase1";
|
|
38
|
+
const passphrase2 = "passphrase2";
|
|
39
|
+
|
|
40
|
+
const address1 = NeuraiKey.getAddressPair(network, mnemonic, 0, 0, passphrase1);
|
|
41
|
+
const address2 = NeuraiKey.getAddressPair(network, mnemonic, 0, 0, passphrase2);
|
|
42
|
+
|
|
43
|
+
expect(address1.external.address).not.toBe(address2.external.address);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("Empty passphrase equals no passphrase", () => {
|
|
47
|
+
const network = "xna";
|
|
48
|
+
const mnemonic = "result pact model attract result puzzle final boss private educate luggage era";
|
|
49
|
+
|
|
50
|
+
const addressWithEmpty = NeuraiKey.getAddressPair(network, mnemonic, 0, 1, "");
|
|
51
|
+
const addressWithoutPassphrase = NeuraiKey.getAddressPair(network, mnemonic, 0, 1);
|
|
52
|
+
|
|
53
|
+
expect(addressWithEmpty.external.address).toBe(addressWithoutPassphrase.external.address);
|
|
54
|
+
expect(addressWithEmpty.external.address).toBe("NLdcSXGQvCVf2RTKhx7GZom34f1JADhBTp");
|
|
22
55
|
});
|
|
23
56
|
|
|
24
57
|
test("Validate Wallet Import Format (WIF) main-net ", () => {
|
|
25
58
|
const network = "xna";
|
|
26
|
-
const mnemonic =
|
|
27
|
-
"orphan resemble brain dwarf bus fancy horn among cricket logic duty crater";
|
|
59
|
+
const mnemonic = "result pact model attract result puzzle final boss private educate luggage era";
|
|
28
60
|
const address = NeuraiKey.getAddressPair(network, mnemonic, 0, 1);
|
|
29
61
|
|
|
30
|
-
expect(address.internal.address).toBe("
|
|
31
|
-
expect(address.external.WIF).toBe(
|
|
32
|
-
|
|
62
|
+
expect(address.internal.address).toBe("NQM5zP6jkwDgCZ2UQiUicW4e3YcWc4NY4S");
|
|
63
|
+
expect(address.external.WIF).toBe("KwWavecys1Qskgzwsyv6CNeTospWkvMeLzx3dLqeV4xAJEMXF8Qq");
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("Convert external public key to main-net address", () => {
|
|
67
|
+
const mnemonic = "result pact model attract result puzzle final boss private educate luggage era";
|
|
68
|
+
const pair = NeuraiKey.getAddressPair("xna", mnemonic, 0, 1);
|
|
69
|
+
|
|
70
|
+
expect(NeuraiKey.publicKeyToAddress("xna", pair.external.publicKey)).toBe(
|
|
71
|
+
pair.external.address
|
|
33
72
|
);
|
|
34
73
|
});
|
|
35
74
|
|
|
36
75
|
test("Validate Wallet Import Format (WIF) test-net ", () => {
|
|
37
76
|
const network = "xna-test";
|
|
38
|
-
const mnemonic =
|
|
39
|
-
"orphan resemble brain dwarf bus fancy horn among cricket logic duty crater";
|
|
77
|
+
const mnemonic = "result pact model attract result puzzle final boss private educate luggage era";
|
|
40
78
|
const address = NeuraiKey.getAddressPair(network, mnemonic, 0, 1);
|
|
41
79
|
|
|
42
|
-
expect(address.external.WIF).toBe(
|
|
43
|
-
|
|
80
|
+
expect(address.external.WIF).toBe("cSfwLzc9DNj4PdzyGK1sAZzxNwih2HaezMrT8w4MXyhf8qhaHJiE");
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("Convert external public key to test-net address", () => {
|
|
84
|
+
const mnemonic = "result pact model attract result puzzle final boss private educate luggage era";
|
|
85
|
+
const pair = NeuraiKey.getAddressPair("xna-test", mnemonic, 0, 1);
|
|
86
|
+
|
|
87
|
+
expect(NeuraiKey.publicKeyToAddress("xna-test", pair.external.publicKey)).toBe(
|
|
88
|
+
pair.external.address
|
|
44
89
|
);
|
|
45
90
|
});
|
|
46
91
|
|
|
47
92
|
test("Validate get public address from Wallet Import Format (WIF) main-net ", () => {
|
|
48
93
|
const network = "xna";
|
|
49
|
-
const WIF = "
|
|
94
|
+
const WIF = "KwWavecys1Qskgzwsyv6CNeTospWkvMeLzx3dLqeV4xAJEMXF8Qq";
|
|
50
95
|
const addressObject = NeuraiKey.getAddressByWIF(network, WIF);
|
|
51
96
|
|
|
52
|
-
expect(addressObject.address).toBe("
|
|
97
|
+
expect(addressObject.address).toBe("NLdcSXGQvCVf2RTKhx7GZom34f1JADhBTp");
|
|
53
98
|
});
|
|
54
99
|
|
|
55
100
|
test("Valid bytes to mnemonic", () => {
|
|
@@ -73,14 +118,14 @@ test("Non valid bytes to mnemonic should fail", () => {
|
|
|
73
118
|
describe("Validate diff languages", () => {
|
|
74
119
|
it("Should accept spanish mnemonic", () => {
|
|
75
120
|
const m =
|
|
76
|
-
"velero nuera pepino
|
|
121
|
+
"velero nuera pepino reír barro reforma negar rumbo atento separar pesa puma";
|
|
77
122
|
const valid = NeuraiKey.isMnemonicValid(m);
|
|
78
123
|
expect(valid).toBe(true);
|
|
79
124
|
});
|
|
80
125
|
|
|
81
126
|
it("Should accept French mnemonic", () => {
|
|
82
127
|
const m =
|
|
83
|
-
"vaseux mixte ozone
|
|
128
|
+
"vaseux mixte ozone quiétude besogne punaise membre réussir avarice samedi pantalon poney";
|
|
84
129
|
const valid = NeuraiKey.isMnemonicValid(m);
|
|
85
130
|
expect(valid).toBe(true);
|
|
86
131
|
});
|
|
@@ -123,4 +168,4 @@ describe("generateAddress", () => {
|
|
|
123
168
|
});
|
|
124
169
|
|
|
125
170
|
// Add more tests if needed to cover different scenarios
|
|
126
|
-
});
|
|
171
|
+
});
|