-
Notifications
You must be signed in to change notification settings - Fork 1
/
ton-mnemonic-pk.html
191 lines (168 loc) · 6.58 KB
/
ton-mnemonic-pk.html
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
<html>
<head>
<script src="tonweb-mnemonic.js"></script>
<script src="jszip.js"></script>
<script src="FileSaver.js"></script>
<script>
const tonMnemonic = window.TonWeb.mnemonic;
async function loadMnemonic(text, num_pks, pk_start, derivation_path) {
let zip = new JSZip();
const pk_end = pk_start + num_pks
for (let i = pk_start; i < pk_end; i++) {
let pk = await tonMnemonic.mnemonicToSeed(text, `${derivation_path}-${i}`);
zip.file(`my-wallet-${i}.pk`, Uint8ToBase64(pk), {base64: true});
}
let pk = await tonMnemonic.mnemonicToSeed(text);
const forchecksum = await crypto.subtle.digest('SHA-256', pk);
const forchecksum16 = Array.from(new Uint16Array(forchecksum));
let checksum_word1 = tonMnemonic.wordlists.EN[forchecksum16[0] & 2047];
let checksum_word2 = tonMnemonic.wordlists.EN[forchecksum16[1] & 2047];
zip.file(`checksum.txt`, `${checksum_word1} ${checksum_word2}`, {base64: false});
zip.generateAsync({type:"blob"})
.then(function(content) {
saveAs(content, "my-wallets.zip");
});
document.querySelector('#checksum').innerText = `Checksum: ${checksum_word1} ${checksum_word2}`;
}
function isWordInList(word) {
let foundArr = tonMnemonic.wordlists.EN.filter((it)=> {
return it == word;
})
return foundArr.length > 0;
}
window.addEventListener("load", async ()=> {
document.querySelector('#load-btn').addEventListener('click', async () => {
let textRaw = document.querySelector('#words').value;
let num_pks = Number(document.querySelector('#num-pks').value);
let pk_start = Number(document.querySelector('#pk-start').value);
let derivation_path = document.querySelector('#derivation-path').value;
if (pk_start < 0) {
document.querySelector('#pk-start-validation-error').innerText = `Error: index should be greater than or equal to zero`;
return;
}
if (num_pks <= 0) {
document.querySelector('#num-pks-validation-error').innerText = `Error: number of private keys to generate should be greater than zero`;
return;
}
textRaw = textRaw.replaceAll('\n', ' ');
const text = textRaw.split(' ');
let anyWordIsNotInList = false;
let badWordIndex = -1;
text.forEach( (it, i)=> {
if(!isWordInList(it)) {
anyWordIsNotInList = true;
badWordIndex = i;
}
})
if(anyWordIsNotInList) {
document.querySelector('#validation-error').innerText = `Mnemonic error: word "${text[badWordIndex]}" is not in the word list`;
return;
}
if( text.length !== 24) {
document.querySelector('#validation-error').innerText = 'Mnemonic error: number of words should be 24';
return;
}
if (! await tonMnemonic.validateMnemonic(text)) {
document.querySelector('#validation-error').innerText = 'Mnemonic error: checksum failed';
return;
}
loadMnemonic(text, num_pks, pk_start, derivation_path);
});
document.querySelector('#gen-seed-btn').addEventListener('click', async ()=> {
const mnemonic = await tonMnemonic.generateMnemonic();
document.querySelector('#words').value = mnemonic.join(' ');
});
})
function Uint8ToBase64(u8Arr){
var CHUNK_SIZE = 0x8000; //arbitrary number
var index = 0;
var length = u8Arr.length;
var result = '';
var slice;
while (index < length) {
slice = u8Arr.subarray(index, Math.min(index + CHUNK_SIZE, length));
result += String.fromCharCode.apply(null, slice);
index += CHUNK_SIZE;
}
document.querySelector('#validation-error').innerText = '';
return btoa(result);
}
</script>
<style>
body {
padding: 20px;
font-size: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}
button {
border: 1px solid rgb(105, 105, 105);
padding: 6px 10px;
margin: 7px;
font-size: 18px;
}
.container {
max-width: 800px;
margin: 10px auto;
}
#validation-error {
color:red;
line-height: 2;
min-height: 40px;
}
#pk-start-validation-error {
color:red;
line-height: 2;
min-height: 40px;
}
#num-pks-validation-error {
color:red;
line-height: 2;
min-height: 40px;
}
#binary-data {
font-size: 14px;
line-height: 2;
min-height: 20px;
}
#checksum {
line-height: 2;
}
a {
text-decoration:cornflowerblue;
}
a:hover {
text-decoration:underline;
}
</style>
</head>
<body>
<div class="container">
<h2>TON Offline Multi Mnemonic Tool</h2>
<br>
<b>1.</b> Enter your 24 word mnemonic:
<br><br>
<textarea name="" id="words" cols="30" rows="10"></textarea>
<br>
or <button id="gen-seed-btn">Generate New Mnemonic</button>
<br>
<div id="validation-error"></div>
<b>2.</b> Enter Derivation Path:
<br><br>
<div><textarea name="" id="derivation-path" cols="10"></textarea></div>
<br><br>
<b>3.</b> Enter number of private keys to generate:
<br><br>
<div><textarea name="" id="num-pks" cols="10"></textarea></div>
<div id="num-pks-validation-error"></div>
<b>4.</b> Enter private key start index:
<br><br>
<div><textarea name="" id="pk-start" cols="10"></textarea></div>
<div id="pk-start-validation-error"></div>
<b>5.</b> Convert 24 word mnemonic to private keys:
<br><br>
<div id="checksum"></div>
<button id="load-btn">Download my-wallets.zip</button>
<br>
</div>
</body>
</html>