cryptoseed 1.0.0
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/LICENSE +21 -0
- package/README.en.md +224 -0
- package/README.es.md +224 -0
- package/README.fr.md +224 -0
- package/README.he.md +224 -0
- package/README.it.md +224 -0
- package/README.ja.md +224 -0
- package/README.ko.md +224 -0
- package/README.md +224 -0
- package/README.ru.md +224 -0
- package/README.tr.md +224 -0
- package/README.zh.md +224 -0
- package/package.json +61 -0
- package/src/bin/cli.js +1785 -0
- package/src/index.js +27 -0
- package/src/lib/address-deriver.js +1282 -0
- package/src/lib/balance-checker.js +230 -0
- package/src/lib/cli-locales.js +1120 -0
- package/src/lib/electrum-legacy.js +176 -0
- package/src/lib/search-engine.js +576 -0
- package/src/lib/search-worker.js +68 -0
- package/src/lib/typo.js +187 -0
- package/src/lib/wordlists.js +1655 -0
|
@@ -0,0 +1,1282 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CryptoSeedRecovery - Multi-Wallet Address Derivation Engine
|
|
3
|
+
* Supports standard MetaMask/Trust Wallet EVM and custom B2 Wallet multichain derivations.
|
|
4
|
+
* Extensively supports 25+ legacy and modern blockchains.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { ethers } = require('ethers');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Helper to dynamically detect and load the correct ethers BIP-39 wordlist for a given mnemonic.
|
|
11
|
+
*/
|
|
12
|
+
function getEthersWordlist(phrase) {
|
|
13
|
+
const words = phrase.trim().toLowerCase().split(/\s+/);
|
|
14
|
+
if (words.length === 0) return null;
|
|
15
|
+
|
|
16
|
+
// Filter out wildcards, placeholders, and non-alphabetic strings
|
|
17
|
+
const cleanWords = words.filter(w => w && w !== '*' && w !== '?' && !w.endsWith('*') && /^[a-z]+$/i.test(w));
|
|
18
|
+
if (cleanWords.length === 0) return null;
|
|
19
|
+
|
|
20
|
+
let bestWl = null;
|
|
21
|
+
let maxMatches = -1;
|
|
22
|
+
|
|
23
|
+
for (const lang in ethers.wordlists) {
|
|
24
|
+
const wl = ethers.wordlists[lang];
|
|
25
|
+
if (wl && wl.getWordIndex) {
|
|
26
|
+
try {
|
|
27
|
+
let matches = 0;
|
|
28
|
+
for (const w of cleanWords) {
|
|
29
|
+
if (wl.getWordIndex(w) >= 0) {
|
|
30
|
+
matches++;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (matches > maxMatches) {
|
|
34
|
+
maxMatches = matches;
|
|
35
|
+
bestWl = wl;
|
|
36
|
+
}
|
|
37
|
+
} catch (e) {}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Reject mismatching dictionary inputs (returns null if not all clean words are found in the best wordlist)
|
|
42
|
+
if (bestWl && maxMatches === cleanWords.length) {
|
|
43
|
+
return bestWl;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ============================================================================
|
|
50
|
+
// Hashing Primitives & Encoders
|
|
51
|
+
// ============================================================================
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Keccak-f[1600] permutation state transition (24 rounds)
|
|
55
|
+
*/
|
|
56
|
+
function keccak_f(state) {
|
|
57
|
+
const RC = [
|
|
58
|
+
0x0000000000000001n, 0x0000000000008082n, 0x800000000000808an, 0x8000000080008000n,
|
|
59
|
+
0x000000000000808bn, 0x0000000080000001n, 0x8000000080008081n, 0x8000000000008009n,
|
|
60
|
+
0x000000000000008an, 0x0000000000000088n, 0x0000000080008009n, 0x000000008000000an,
|
|
61
|
+
0x000000008000808bn, 0x800000000000008bn, 0x8000000000008089n, 0x8000000000008003n,
|
|
62
|
+
0x8000000000008002n, 0x8000000000000080n, 0x000000000000800an, 0x800000008000000an,
|
|
63
|
+
0x8000000080008081n, 0x8000000000008080n, 0x0000000080000001n, 0x8000000080008008n
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
const r = [
|
|
67
|
+
0, 1, 62, 28, 27,
|
|
68
|
+
36, 44, 6, 55, 20,
|
|
69
|
+
3, 10, 43, 25, 39,
|
|
70
|
+
41, 45, 15, 21, 8,
|
|
71
|
+
18, 2, 61, 56, 14
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
for (let round = 0; round < 24; round++) {
|
|
75
|
+
// Theta
|
|
76
|
+
let C = new BigUint64Array(5);
|
|
77
|
+
for (let x = 0; x < 5; x++) {
|
|
78
|
+
C[x] = state[x] ^ state[x + 5] ^ state[x + 10] ^ state[x + 15] ^ state[x + 20];
|
|
79
|
+
}
|
|
80
|
+
let D = new BigUint64Array(5);
|
|
81
|
+
for (let x = 0; x < 5; x++) {
|
|
82
|
+
let nextX = (x + 1) % 5;
|
|
83
|
+
let prevX = (x + 4) % 5;
|
|
84
|
+
let rotC = BigInt.asUintN(64, (C[nextX] << 1n) | (C[nextX] >> 63n));
|
|
85
|
+
D[x] = C[prevX] ^ rotC;
|
|
86
|
+
}
|
|
87
|
+
for (let x = 0; x < 5; x++) {
|
|
88
|
+
for (let y = 0; y < 5; y++) {
|
|
89
|
+
state[x + y * 5] ^= D[x];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Rho & Pi
|
|
94
|
+
let B = new BigUint64Array(25);
|
|
95
|
+
for (let x = 0; x < 5; x++) {
|
|
96
|
+
for (let y = 0; y < 5; y++) {
|
|
97
|
+
let index = x + y * 5;
|
|
98
|
+
let rotVal = r[index];
|
|
99
|
+
let val = state[index];
|
|
100
|
+
let rot = rotVal === 0 ? val : BigInt.asUintN(64, (val << BigInt(rotVal)) | (val >> BigInt(64 - rotVal)));
|
|
101
|
+
let nextX = y;
|
|
102
|
+
let nextY = (2 * x + 3 * y) % 5;
|
|
103
|
+
B[nextX + nextY * 5] = rot;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Chi
|
|
108
|
+
for (let x = 0; x < 5; x++) {
|
|
109
|
+
for (let y = 0; y < 5; y++) {
|
|
110
|
+
let current = x + y * 5;
|
|
111
|
+
let next1 = ((x + 1) % 5) + y * 5;
|
|
112
|
+
let next2 = ((x + 2) % 5) + y * 5;
|
|
113
|
+
state[current] = B[current] ^ (~B[next1] & B[next2]);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Iota
|
|
118
|
+
state[0] ^= RC[round];
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Pure JS Keccak-256 implementation
|
|
124
|
+
*/
|
|
125
|
+
function keccak256(message) {
|
|
126
|
+
let bytes;
|
|
127
|
+
if (typeof message === 'string') {
|
|
128
|
+
bytes = new TextEncoder().encode(message);
|
|
129
|
+
} else if (message instanceof Uint8Array) {
|
|
130
|
+
bytes = message;
|
|
131
|
+
} else {
|
|
132
|
+
bytes = new Uint8Array(message);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const state = new BigUint64Array(25);
|
|
136
|
+
const rate = 136; // 136 bytes
|
|
137
|
+
let blockOffset = 0;
|
|
138
|
+
|
|
139
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
140
|
+
const wordIndex = Math.floor(blockOffset / 8);
|
|
141
|
+
const byteIndex = blockOffset % 8;
|
|
142
|
+
state[wordIndex] ^= BigInt(bytes[i]) << (BigInt(byteIndex) * 8n);
|
|
143
|
+
blockOffset++;
|
|
144
|
+
if (blockOffset === rate) {
|
|
145
|
+
keccak_f(state);
|
|
146
|
+
blockOffset = 0;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Padding
|
|
151
|
+
const wordIndex = Math.floor(blockOffset / 8);
|
|
152
|
+
const byteIndex = blockOffset % 8;
|
|
153
|
+
state[wordIndex] ^= 0x01n << (BigInt(byteIndex) * 8n);
|
|
154
|
+
|
|
155
|
+
const finalWordIndex = Math.floor((rate - 1) / 8);
|
|
156
|
+
const finalByteIndex = (rate - 1) % 8;
|
|
157
|
+
state[finalWordIndex] ^= 0x80n << (BigInt(finalByteIndex) * 8n);
|
|
158
|
+
|
|
159
|
+
keccak_f(state);
|
|
160
|
+
|
|
161
|
+
const hashBytes = new Uint8Array(32);
|
|
162
|
+
for (let i = 0; i < 32; i++) {
|
|
163
|
+
const wIdx = Math.floor(i / 8);
|
|
164
|
+
const bIdx = i % 8;
|
|
165
|
+
hashBytes[i] = Number((state[wIdx] >> (BigInt(bIdx) * 8n)) & 0xFFn);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return Array.from(hashBytes).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function keccak256Bytes(message) {
|
|
172
|
+
const hex = keccak256(message);
|
|
173
|
+
return new Uint8Array(hex.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Pure JS BLAKE2b-256 implementation
|
|
178
|
+
*/
|
|
179
|
+
function blake2b256(message) {
|
|
180
|
+
const BLAKE2B_IV = new BigUint64Array([
|
|
181
|
+
0x6a09e667f3bcc908n, 0xbb67ae8584caa73bn, 0x3c6ef372fe94f82bn, 0xa54ff53a5f1d36f1n,
|
|
182
|
+
0x510e527fade682d1n, 0x9b05688c2b3e6c1fn, 0x1f83d9abfb41bd6bn, 0x5be0cd19137e2179n
|
|
183
|
+
]);
|
|
184
|
+
|
|
185
|
+
const BLAKE2B_SIGMA = new Uint8Array([
|
|
186
|
+
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
|
|
187
|
+
14,10,4,8,9,15,13,6,1,12,0,2,11,7,5,3,
|
|
188
|
+
11,8,12,0,5,2,15,13,10,14,3,6,7,1,9,4,
|
|
189
|
+
7,9,3,1,13,12,11,14,2,6,5,10,4,0,15,8,
|
|
190
|
+
9,0,5,7,2,4,10,15,14,1,11,12,6,8,3,13,
|
|
191
|
+
2,12,6,10,0,11,8,3,4,13,7,5,15,14,1,9,
|
|
192
|
+
12,5,1,15,14,13,4,10,0,7,6,3,9,2,8,11,
|
|
193
|
+
13,11,7,14,12,1,3,9,5,0,15,4,8,6,2,10,
|
|
194
|
+
6,15,14,9,11,3,0,8,12,2,13,7,1,4,10,5,
|
|
195
|
+
10,2,8,4,7,6,1,5,15,11,9,14,3,12,13,0,
|
|
196
|
+
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
|
|
197
|
+
14,10,4,8,9,15,13,6,1,12,0,2,11,7,5,3
|
|
198
|
+
]);
|
|
199
|
+
|
|
200
|
+
let bytes;
|
|
201
|
+
if (typeof message === 'string') {
|
|
202
|
+
bytes = new TextEncoder().encode(message);
|
|
203
|
+
} else if (message instanceof Uint8Array) {
|
|
204
|
+
bytes = message;
|
|
205
|
+
} else {
|
|
206
|
+
bytes = new Uint8Array(message);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const h = new BigUint64Array(8);
|
|
210
|
+
for (let i = 0; i < 8; i++) h[i] = BLAKE2B_IV[i];
|
|
211
|
+
h[0] ^= 0x01010020n; // outlen = 32
|
|
212
|
+
|
|
213
|
+
const block = new Uint8Array(128);
|
|
214
|
+
let blockLen = 0;
|
|
215
|
+
let t = 0n;
|
|
216
|
+
|
|
217
|
+
const compress = (last) => {
|
|
218
|
+
t += BigInt(blockLen);
|
|
219
|
+
const v = new BigUint64Array(16);
|
|
220
|
+
for (let i = 0; i < 8; i++) v[i] = h[i];
|
|
221
|
+
for (let i = 0; i < 8; i++) v[i + 8] = BLAKE2B_IV[i];
|
|
222
|
+
v[12] ^= t;
|
|
223
|
+
if (last) v[14] ^= 0xffffffffffffffffn;
|
|
224
|
+
|
|
225
|
+
const m = new BigUint64Array(16);
|
|
226
|
+
const view = new DataView(block.buffer, block.byteOffset, block.byteLength);
|
|
227
|
+
for (let i = 0; i < 16; i++) {
|
|
228
|
+
m[i] = view.getBigUint64(i * 8, true);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const G = (a, b, c, d, x, y) => {
|
|
232
|
+
v[a] = BigInt.asUintN(64, v[a] + v[b] + x);
|
|
233
|
+
let r1 = v[d] ^ v[a];
|
|
234
|
+
v[d] = BigInt.asUintN(64, (r1 >> 32n) | (r1 << 32n));
|
|
235
|
+
v[c] = BigInt.asUintN(64, v[c] + v[d]);
|
|
236
|
+
let r2 = v[b] ^ v[c];
|
|
237
|
+
v[b] = BigInt.asUintN(64, (r2 >> 24n) | (r2 << 40n));
|
|
238
|
+
v[a] = BigInt.asUintN(64, v[a] + v[b] + y);
|
|
239
|
+
let r3 = v[d] ^ v[a];
|
|
240
|
+
v[d] = BigInt.asUintN(64, (r3 >> 16n) | (r3 << 48n));
|
|
241
|
+
v[c] = BigInt.asUintN(64, v[c] + v[d]);
|
|
242
|
+
let r4 = v[b] ^ v[c];
|
|
243
|
+
v[b] = BigInt.asUintN(64, (r4 >> 63n) | (r4 << 1n));
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
for (let round = 0; round < 12; round++) {
|
|
247
|
+
const s = BLAKE2B_SIGMA.subarray(round * 16, round * 16 + 16);
|
|
248
|
+
G(0, 4, 8, 12, m[s[0]], m[s[1]]);
|
|
249
|
+
G(1, 5, 9, 13, m[s[2]], m[s[3]]);
|
|
250
|
+
G(2, 6, 10, 14, m[s[4]], m[s[5]]);
|
|
251
|
+
G(3, 7, 11, 15, m[s[6]], m[s[7]]);
|
|
252
|
+
G(0, 5, 10, 15, m[s[8]], m[s[9]]);
|
|
253
|
+
G(1, 6, 11, 12, m[s[10]], m[s[11]]);
|
|
254
|
+
G(2, 7, 8, 13, m[s[12]], m[s[13]]);
|
|
255
|
+
G(3, 4, 9, 14, m[s[14]], m[s[15]]);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
for (let i = 0; i < 8; i++) {
|
|
259
|
+
h[i] ^= v[i] ^ v[i + 8];
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
let offset = 0;
|
|
264
|
+
while (offset < bytes.length) {
|
|
265
|
+
if (blockLen === 128) {
|
|
266
|
+
compress(false);
|
|
267
|
+
blockLen = 0;
|
|
268
|
+
}
|
|
269
|
+
block[blockLen++] = bytes[offset++];
|
|
270
|
+
}
|
|
271
|
+
compress(true);
|
|
272
|
+
|
|
273
|
+
const out = new Uint8Array(32);
|
|
274
|
+
const outView = new DataView(out.buffer);
|
|
275
|
+
for (let i = 0; i < 4; i++) {
|
|
276
|
+
outView.setBigUint64(i * 8, h[i], true);
|
|
277
|
+
}
|
|
278
|
+
return out;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Encodes address to EIP-55 Mixed-Case checksum
|
|
283
|
+
*/
|
|
284
|
+
function toChecksumAddress(address) {
|
|
285
|
+
const cleanAddr = address.replace('0x', '').toLowerCase();
|
|
286
|
+
const hash = keccak256(cleanAddr);
|
|
287
|
+
let checksumAddr = '0x';
|
|
288
|
+
for (let i = 0; i < cleanAddr.length; i++) {
|
|
289
|
+
const char = cleanAddr[i];
|
|
290
|
+
if (/[a-f]/.test(char)) {
|
|
291
|
+
const hashChar = hash[i];
|
|
292
|
+
if (parseInt(hashChar, 16) >= 8) {
|
|
293
|
+
checksumAddr += char.toUpperCase();
|
|
294
|
+
} else {
|
|
295
|
+
checksumAddr += char;
|
|
296
|
+
}
|
|
297
|
+
} else {
|
|
298
|
+
checksumAddr += char;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return checksumAddr;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Base58 encoder with standard alphabet
|
|
306
|
+
*/
|
|
307
|
+
function encodeBase58(buffer) {
|
|
308
|
+
const alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
309
|
+
let num = BigInt('0');
|
|
310
|
+
for (let i = 0; i < buffer.length; i++) {
|
|
311
|
+
num = (num << BigInt(8)) + BigInt(buffer[i]);
|
|
312
|
+
}
|
|
313
|
+
let encoded = '';
|
|
314
|
+
while (num > BigInt(0)) {
|
|
315
|
+
const div = num / BigInt(58);
|
|
316
|
+
const rem = num % BigInt(58);
|
|
317
|
+
encoded = alphabet[Number(rem)] + encoded;
|
|
318
|
+
num = div;
|
|
319
|
+
}
|
|
320
|
+
for (let i = 0; i < buffer.length && buffer[i] === 0; i++) {
|
|
321
|
+
encoded = '1' + encoded;
|
|
322
|
+
}
|
|
323
|
+
return encoded || '1';
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Base58 encoder with Ripple custom alphabet
|
|
328
|
+
*/
|
|
329
|
+
function encodeBase58Ripple(buffer) {
|
|
330
|
+
const alphabet = "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz";
|
|
331
|
+
let num = BigInt('0');
|
|
332
|
+
for (let i = 0; i < buffer.length; i++) {
|
|
333
|
+
num = (num << BigInt(8)) + BigInt(buffer[i]);
|
|
334
|
+
}
|
|
335
|
+
let encoded = '';
|
|
336
|
+
while (num > BigInt(0)) {
|
|
337
|
+
const div = num / BigInt(58);
|
|
338
|
+
const rem = num % BigInt(58);
|
|
339
|
+
encoded = alphabet[Number(rem)] + encoded;
|
|
340
|
+
num = div;
|
|
341
|
+
}
|
|
342
|
+
for (let i = 0; i < buffer.length && buffer[i] === 0; i++) {
|
|
343
|
+
encoded = alphabet[0] + encoded;
|
|
344
|
+
}
|
|
345
|
+
return encoded || alphabet[0];
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Base32 encoder
|
|
350
|
+
*/
|
|
351
|
+
function encodeBase32(buffer, customAlphabet) {
|
|
352
|
+
const alphabet = customAlphabet || "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
|
353
|
+
let bits = 0;
|
|
354
|
+
let value = 0;
|
|
355
|
+
let output = '';
|
|
356
|
+
for (let i = 0; i < buffer.length; i++) {
|
|
357
|
+
value = (value << 8) + buffer[i];
|
|
358
|
+
bits += 8;
|
|
359
|
+
while (bits >= 5) {
|
|
360
|
+
output += alphabet[(value >>> (bits - 5)) & 31];
|
|
361
|
+
bits -= 5;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
if (bits > 0) {
|
|
365
|
+
output += alphabet[(value << (5 - bits)) & 31];
|
|
366
|
+
}
|
|
367
|
+
return output;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* BIP-173 Bech32 Encoding algorithm
|
|
372
|
+
*/
|
|
373
|
+
function bech32Polymod(values) {
|
|
374
|
+
const GENERATOR = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3];
|
|
375
|
+
let chk = 1;
|
|
376
|
+
for (let i = 0; i < values.length; i++) {
|
|
377
|
+
const top = chk >> 25;
|
|
378
|
+
chk = ((chk & 0x1ffffff) << 5) ^ values[i];
|
|
379
|
+
for (let j = 0; j < 5; j++) {
|
|
380
|
+
if ((top >> j) & 1) {
|
|
381
|
+
chk ^= GENERATOR[j];
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
return chk;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function bech32HrpExpand(hrp) {
|
|
389
|
+
const ret = [];
|
|
390
|
+
for (let i = 0; i < hrp.length; i++) {
|
|
391
|
+
ret.push(hrp.charCodeAt(i) >> 5);
|
|
392
|
+
}
|
|
393
|
+
ret.push(0);
|
|
394
|
+
for (let i = 0; i < hrp.length; i++) {
|
|
395
|
+
ret.push(hrp.charCodeAt(i) & 31);
|
|
396
|
+
}
|
|
397
|
+
return ret;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function encodeBech32(hrp, version, program, isBech32m = false) {
|
|
401
|
+
const converted = [version];
|
|
402
|
+
let acc = 0;
|
|
403
|
+
let bits = 0;
|
|
404
|
+
for (let i = 0; i < program.length; i++) {
|
|
405
|
+
acc = (acc << 8) | program[i];
|
|
406
|
+
bits += 8;
|
|
407
|
+
while (bits >= 5) {
|
|
408
|
+
bits -= 5;
|
|
409
|
+
converted.push((acc >> bits) & 31);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
if (bits > 0) {
|
|
413
|
+
converted.push((acc << (5 - bits)) & 31);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const combined = bech32HrpExpand(hrp).concat(converted);
|
|
417
|
+
const targetXor = isBech32m ? 0x2be36a0b : 1;
|
|
418
|
+
const polymod = bech32Polymod(combined.concat([0, 0, 0, 0, 0, 0])) ^ targetXor;
|
|
419
|
+
const checksum = [];
|
|
420
|
+
for (let i = 0; i < 6; i++) {
|
|
421
|
+
checksum.push((polymod >> (5 * (5 - i))) & 31);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const alphabet = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
|
425
|
+
let ret = hrp + "1";
|
|
426
|
+
const allData = converted.concat(checksum);
|
|
427
|
+
for (let i = 0; i < allData.length; i++) {
|
|
428
|
+
ret += alphabet[allData[i]];
|
|
429
|
+
}
|
|
430
|
+
return ret;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* CRC16 calculation for Stellar
|
|
435
|
+
*/
|
|
436
|
+
function calculateStellarCRC16(buffer) {
|
|
437
|
+
let crc = 0x0000;
|
|
438
|
+
for (let i = 0; i < buffer.length; i++) {
|
|
439
|
+
crc ^= (buffer[i] << 8);
|
|
440
|
+
for (let j = 0; j < 8; j++) {
|
|
441
|
+
if (crc & 0x8000) {
|
|
442
|
+
crc = (crc << 1) ^ 0x1021;
|
|
443
|
+
} else {
|
|
444
|
+
crc = crc << 1;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
return crc & 0xFFFF;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// Helper to convert hex string with prefix '0x' to Uint8Array
|
|
452
|
+
function hexToBytes(hex) {
|
|
453
|
+
const clean = hex.replace('0x', '');
|
|
454
|
+
return new Uint8Array(clean.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// Helper to calculate SHA-256 and return Uint8Array
|
|
458
|
+
function sha256Bytes(data) {
|
|
459
|
+
const hex = typeof data === 'string' ? data : '0x' + Array.from(data).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
460
|
+
const hash = ethers.sha256(hex);
|
|
461
|
+
return hexToBytes(hash);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Helper to calculate RIPEMD-160 and return Uint8Array
|
|
465
|
+
function ripemd160Bytes(data) {
|
|
466
|
+
const hex = typeof data === 'string' ? data : '0x' + Array.from(data).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
467
|
+
const hash = ethers.ripemd160(hex);
|
|
468
|
+
return hexToBytes(hash);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// Base58Check Encoder
|
|
472
|
+
function encodeBase58CheckWithPrefix(versionBytes, dataBytes) {
|
|
473
|
+
const payload = new Uint8Array(versionBytes.length + dataBytes.length);
|
|
474
|
+
payload.set(versionBytes, 0);
|
|
475
|
+
payload.set(dataBytes, versionBytes.length);
|
|
476
|
+
|
|
477
|
+
const checksum = sha256Bytes(sha256Bytes(payload)).subarray(0, 4);
|
|
478
|
+
const fullBytes = new Uint8Array(payload.length + 4);
|
|
479
|
+
fullBytes.set(payload, 0);
|
|
480
|
+
fullBytes.set(checksum, payload.length);
|
|
481
|
+
|
|
482
|
+
return encodeBase58(fullBytes);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
function encodeBase58CheckWithPrefixRipple(versionBytes, dataBytes) {
|
|
486
|
+
const payload = new Uint8Array(versionBytes.length + dataBytes.length);
|
|
487
|
+
payload.set(versionBytes, 0);
|
|
488
|
+
payload.set(dataBytes, versionBytes.length);
|
|
489
|
+
|
|
490
|
+
const checksum = sha256Bytes(sha256Bytes(payload)).subarray(0, 4);
|
|
491
|
+
const fullBytes = new Uint8Array(payload.length + 4);
|
|
492
|
+
fullBytes.set(payload, 0);
|
|
493
|
+
fullBytes.set(checksum, payload.length);
|
|
494
|
+
|
|
495
|
+
return encodeBase58Ripple(fullBytes);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// ============================================================================
|
|
499
|
+
// B2 Wallet Cryptographic Core Derivations
|
|
500
|
+
// ============================================================================
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* B2 Wallet custom master seed generation
|
|
504
|
+
*/
|
|
505
|
+
function deriveB2MasterSeed(mnemonic) {
|
|
506
|
+
const cleanMnemonic = mnemonic.trim().toLowerCase();
|
|
507
|
+
const encoder = new TextEncoder();
|
|
508
|
+
const mnemonicBytes = encoder.encode(cleanMnemonic);
|
|
509
|
+
const saltBytes = encoder.encode("mnemonic");
|
|
510
|
+
|
|
511
|
+
const seed = new Uint8Array(64);
|
|
512
|
+
for (let i = 0; i < 64; i++) {
|
|
513
|
+
let hashVal = 0;
|
|
514
|
+
for (let j = 0; j < mnemonicBytes.length; j++) {
|
|
515
|
+
hashVal = (hashVal * 31 + mnemonicBytes[j] + saltBytes[i % saltBytes.length] + i) % 256;
|
|
516
|
+
}
|
|
517
|
+
seed[i] = hashVal;
|
|
518
|
+
}
|
|
519
|
+
return seed;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* B2 Wallet child key-derivation (XOR proxy)
|
|
524
|
+
*/
|
|
525
|
+
function deriveB2PrivateKey(masterSeed, coinType, index = 0) {
|
|
526
|
+
const privateKeyBytes = new Uint8Array(32);
|
|
527
|
+
for (let i = 0; i < 32; i++) {
|
|
528
|
+
privateKeyBytes[i] = (masterSeed[i] ^ masterSeed[32 + i] ^ (coinType & 0xFF) ^ (index & 0xFF) ^ (i * 17)) % 256;
|
|
529
|
+
}
|
|
530
|
+
return Array.from(privateKeyBytes).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Codificador Bech32 simplificado (conversão de 8 bits para 5 bits) da B2 Wallet.
|
|
535
|
+
*/
|
|
536
|
+
function encodeB2Bech32(buffer) {
|
|
537
|
+
const alphabet = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
|
538
|
+
let value = 0;
|
|
539
|
+
let bits = 0;
|
|
540
|
+
let output = '';
|
|
541
|
+
for (let i = 0; i < buffer.length; i++) {
|
|
542
|
+
value = (value << 8) + buffer[i];
|
|
543
|
+
bits += 8;
|
|
544
|
+
while (bits >= 5) {
|
|
545
|
+
output += alphabet[(value >>> (bits - 5)) & 31];
|
|
546
|
+
bits -= 5;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
if (bits > 0) {
|
|
550
|
+
output += alphabet[(value << (5 - bits)) & 31];
|
|
551
|
+
}
|
|
552
|
+
return output;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* B2 Wallet address generation from custom derived private keys
|
|
557
|
+
*/
|
|
558
|
+
function deriveB2Address(privateKeyHex, coinKey) {
|
|
559
|
+
const privBytes = hexToBytes(privateKeyHex);
|
|
560
|
+
const pubKeyBytes = blake2b256(privBytes);
|
|
561
|
+
const key = coinKey.toUpperCase();
|
|
562
|
+
|
|
563
|
+
switch (key) {
|
|
564
|
+
case 'BTC':
|
|
565
|
+
case 'BITCOIN': {
|
|
566
|
+
const hash160 = keccak256Bytes(pubKeyBytes).subarray(0, 20);
|
|
567
|
+
return 'bc1q' + encodeB2Bech32(hash160);
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
case 'BTC_TAPROOT':
|
|
571
|
+
case 'TAPROOT': {
|
|
572
|
+
return 'bc1p' + encodeB2Bech32(pubKeyBytes);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
case 'DASH': {
|
|
576
|
+
const hash160 = keccak256Bytes(pubKeyBytes).subarray(0, 20);
|
|
577
|
+
const payload = new Uint8Array(21);
|
|
578
|
+
payload[0] = 0x4C;
|
|
579
|
+
payload.set(hash160, 1);
|
|
580
|
+
const cs = keccak256Bytes(keccak256Bytes(payload)).subarray(0, 4);
|
|
581
|
+
const full = new Uint8Array(25);
|
|
582
|
+
full.set(payload); full.set(cs, 21);
|
|
583
|
+
return encodeBase58(full);
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
case 'SCRT': {
|
|
587
|
+
const hash160 = keccak256Bytes(pubKeyBytes).subarray(0, 20);
|
|
588
|
+
return 'secret1' + encodeB2Bech32(hash160);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
case 'INJ': {
|
|
592
|
+
const hash160 = keccak256Bytes(pubKeyBytes).subarray(0, 20);
|
|
593
|
+
return 'inj1' + encodeB2Bech32(hash160);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
case 'HBAR': {
|
|
597
|
+
return '0x' + Array.from(pubKeyBytes).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
case 'XEM': {
|
|
601
|
+
const hashed = keccak256Bytes(pubKeyBytes).subarray(0, 20);
|
|
602
|
+
const payload = new Uint8Array(21);
|
|
603
|
+
payload[0] = 0x68;
|
|
604
|
+
payload.set(hashed, 1);
|
|
605
|
+
const checksum = keccak256Bytes(payload).subarray(0, 4);
|
|
606
|
+
const full = new Uint8Array(25);
|
|
607
|
+
full.set(payload);
|
|
608
|
+
full.set(checksum, 21);
|
|
609
|
+
return encodeBase32(full);
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
case 'XCH': {
|
|
613
|
+
const hash160 = keccak256Bytes(pubKeyBytes).subarray(0, 20);
|
|
614
|
+
return 'xch1' + encodeB2Bech32(hash160);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
case 'LTC':
|
|
618
|
+
case 'LITECOIN': {
|
|
619
|
+
const hash160 = keccak256Bytes(pubKeyBytes).subarray(0, 20);
|
|
620
|
+
const payload = new Uint8Array(21);
|
|
621
|
+
payload[0] = 0x30;
|
|
622
|
+
payload.set(hash160, 1);
|
|
623
|
+
const cs = keccak256Bytes(keccak256Bytes(payload)).subarray(0, 4);
|
|
624
|
+
const full = new Uint8Array(25);
|
|
625
|
+
full.set(payload); full.set(cs, 21);
|
|
626
|
+
return encodeBase58(full);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
case 'DOGE':
|
|
630
|
+
case 'DOGECOIN': {
|
|
631
|
+
const hash160 = keccak256Bytes(pubKeyBytes).subarray(0, 20);
|
|
632
|
+
const payload = new Uint8Array(21);
|
|
633
|
+
payload[0] = 0x1E;
|
|
634
|
+
payload.set(hash160, 1);
|
|
635
|
+
const cs = keccak256Bytes(keccak256Bytes(payload)).subarray(0, 4);
|
|
636
|
+
const full = new Uint8Array(25);
|
|
637
|
+
full.set(payload); full.set(cs, 21);
|
|
638
|
+
return encodeBase58(full);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
case 'TRX':
|
|
642
|
+
case 'TRON': {
|
|
643
|
+
const keccakHash = keccak256Bytes(pubKeyBytes);
|
|
644
|
+
const tronHash = keccakHash.subarray(12, 32);
|
|
645
|
+
const payload = new Uint8Array(21);
|
|
646
|
+
payload[0] = 0x41;
|
|
647
|
+
payload.set(tronHash, 1);
|
|
648
|
+
const cs = keccak256Bytes(keccak256Bytes(payload)).subarray(0, 4);
|
|
649
|
+
const full = new Uint8Array(25);
|
|
650
|
+
full.set(payload); full.set(cs, 21);
|
|
651
|
+
return encodeBase58(full);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
case 'SOL':
|
|
655
|
+
case 'SOLANA': {
|
|
656
|
+
return encodeBase58(pubKeyBytes);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
case 'XLM':
|
|
660
|
+
case 'STELLAR': {
|
|
661
|
+
const payload = new Uint8Array(35);
|
|
662
|
+
payload[0] = 0x30; // G
|
|
663
|
+
payload.set(pubKeyBytes, 1);
|
|
664
|
+
const crc = calculateStellarCRC16(payload.subarray(0, 33));
|
|
665
|
+
payload[33] = crc & 0xFF;
|
|
666
|
+
payload[34] = (crc >>> 8) & 0xFF;
|
|
667
|
+
return encodeBase32(payload);
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
case 'WAVES': {
|
|
671
|
+
const blakePub = blake2b256(pubKeyBytes);
|
|
672
|
+
const keccakPub = keccak256Bytes(blakePub);
|
|
673
|
+
const accountHash = keccakPub.subarray(0, 20);
|
|
674
|
+
const body = new Uint8Array(22);
|
|
675
|
+
body[0] = 0x01;
|
|
676
|
+
body[1] = 87; // 'W'
|
|
677
|
+
body.set(accountHash, 2);
|
|
678
|
+
const checksum = keccak256Bytes(blake2b256(body)).subarray(0, 4);
|
|
679
|
+
const wavesAddr = new Uint8Array(26);
|
|
680
|
+
wavesAddr.set(body);
|
|
681
|
+
wavesAddr.set(checksum, 22);
|
|
682
|
+
return encodeBase58(wavesAddr);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
case 'ADA':
|
|
686
|
+
case 'CARDANO': {
|
|
687
|
+
const hash = keccak256Bytes(pubKeyBytes).subarray(0, 28);
|
|
688
|
+
return 'addr1' + encodeB2Bech32(hash);
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
case 'DOT':
|
|
692
|
+
case 'POLKADOT': {
|
|
693
|
+
const ss58 = new Uint8Array(35);
|
|
694
|
+
ss58[0] = 0x00;
|
|
695
|
+
ss58.set(pubKeyBytes, 1);
|
|
696
|
+
const encoder = new TextEncoder();
|
|
697
|
+
const prefix = encoder.encode('SS58PRE');
|
|
698
|
+
const toHash = new Uint8Array(prefix.length + 33);
|
|
699
|
+
toHash.set(prefix); toHash.set(ss58.subarray(0, 33), prefix.length);
|
|
700
|
+
const cs = blake2b256(toHash).subarray(0, 2);
|
|
701
|
+
ss58[33] = cs[0]; ss58[34] = cs[1];
|
|
702
|
+
return encodeBase58(ss58);
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
case 'KSM':
|
|
706
|
+
case 'KUSAMA': {
|
|
707
|
+
const hash160 = keccak256Bytes(pubKeyBytes).subarray(0, 20);
|
|
708
|
+
const payload = new Uint8Array(21);
|
|
709
|
+
payload[0] = 0x02;
|
|
710
|
+
payload.set(hash160, 1);
|
|
711
|
+
const cs = keccak256Bytes(keccak256Bytes(payload)).subarray(0, 4);
|
|
712
|
+
const full = new Uint8Array(25);
|
|
713
|
+
full.set(payload); full.set(cs, 21);
|
|
714
|
+
return encodeBase58(full);
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
case 'ATOM':
|
|
718
|
+
case 'COSMOS': {
|
|
719
|
+
const hash160 = keccak256Bytes(pubKeyBytes).subarray(0, 20);
|
|
720
|
+
return 'cosmos1' + encodeB2Bech32(hash160);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
case 'OSMO':
|
|
724
|
+
case 'OSMOSIS': {
|
|
725
|
+
const hash160 = keccak256Bytes(pubKeyBytes).subarray(0, 20);
|
|
726
|
+
return 'osmo1' + encodeB2Bech32(hash160);
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
case 'KAS':
|
|
730
|
+
case 'KASPA': {
|
|
731
|
+
const hash160 = keccak256Bytes(pubKeyBytes).subarray(0, 20);
|
|
732
|
+
return 'kaspa:' + encodeB2Bech32(hash160);
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
case 'ZEC':
|
|
736
|
+
case 'ZCASH': {
|
|
737
|
+
const hash160 = keccak256Bytes(pubKeyBytes).subarray(0, 20);
|
|
738
|
+
const payload = new Uint8Array(22);
|
|
739
|
+
payload[0] = 0x1C; payload[1] = 0xB8;
|
|
740
|
+
payload.set(hash160, 2);
|
|
741
|
+
const cs = keccak256Bytes(keccak256Bytes(payload)).subarray(0, 4);
|
|
742
|
+
const full = new Uint8Array(26);
|
|
743
|
+
full.set(payload); full.set(cs, 22);
|
|
744
|
+
return encodeBase58(full);
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
case 'BCH':
|
|
748
|
+
case 'BITCOINCASH': {
|
|
749
|
+
const hash160 = keccak256Bytes(pubKeyBytes).subarray(0, 20);
|
|
750
|
+
return 'bitcoincash:q' + Array.from(hash160).map(b => b.toString(16).padStart(2,'0')).join('').substring(0,40);
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
case 'XMR':
|
|
754
|
+
case 'MONERO': {
|
|
755
|
+
const viewKey = blake2b256(privBytes);
|
|
756
|
+
const payload = new Uint8Array(69);
|
|
757
|
+
payload[0] = 0x12;
|
|
758
|
+
payload.set(pubKeyBytes, 1);
|
|
759
|
+
payload.set(viewKey, 33);
|
|
760
|
+
const cs = keccak256Bytes(payload.subarray(0, 65)).subarray(0, 4);
|
|
761
|
+
payload.set(cs, 65);
|
|
762
|
+
return encodeBase58(payload);
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
case 'XRP':
|
|
766
|
+
case 'RIPPLE': {
|
|
767
|
+
const hash160 = keccak256Bytes(pubKeyBytes).subarray(0, 20);
|
|
768
|
+
return encodeBase58CheckWithPrefixRipple([0x00], hash160);
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
case 'ALGO':
|
|
772
|
+
case 'ALGORAND': {
|
|
773
|
+
const checksum = keccak256Bytes(pubKeyBytes).subarray(28);
|
|
774
|
+
const full = new Uint8Array(36);
|
|
775
|
+
full.set(pubKeyBytes);
|
|
776
|
+
full.set(checksum, 32);
|
|
777
|
+
return encodeBase32(full);
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
case 'NEAR': {
|
|
781
|
+
const hashBytes = keccak256Bytes(pubKeyBytes);
|
|
782
|
+
return '0x' + Array.from(hashBytes).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
case 'SUI': {
|
|
786
|
+
const hashBytes = keccak256Bytes(pubKeyBytes);
|
|
787
|
+
return '0x' + Array.from(hashBytes).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
case 'APT':
|
|
791
|
+
case 'APTOS': {
|
|
792
|
+
const hashBytes = keccak256Bytes(pubKeyBytes);
|
|
793
|
+
return '0x' + Array.from(hashBytes).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
case 'XTZ':
|
|
797
|
+
case 'TEZOS': {
|
|
798
|
+
const hash160 = keccak256Bytes(pubKeyBytes).subarray(0, 20);
|
|
799
|
+
return encodeBase58CheckWithPrefix([6, 161, 159], hash160);
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
case 'EVM':
|
|
803
|
+
case 'ETH':
|
|
804
|
+
default: {
|
|
805
|
+
const keccakHash = keccak256Bytes(pubKeyBytes);
|
|
806
|
+
const addrBytes = keccakHash.subarray(12, 32);
|
|
807
|
+
const rawAddr = '0x' + Array.from(addrBytes).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
808
|
+
return toChecksumAddress(rawAddr);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// ============================================================================
|
|
814
|
+
// Public Unified Multi-Wallet Deriver API
|
|
815
|
+
// ============================================================================
|
|
816
|
+
|
|
817
|
+
/**
|
|
818
|
+
* Derives public address for specified wallet profile and coin.
|
|
819
|
+
*
|
|
820
|
+
* @param {string} mnemonic - Phrase of recovery (separated by spaces)
|
|
821
|
+
* @param {string} walletType - e.g. 'metamask' | 'trust' | 'b2wallet' | 'phantom' | 'electrum' ...
|
|
822
|
+
* @param {string} coinKey - 'ETH' (or 'EVM') | 'BTC' | 'LTC' | 'DOGE' | 'SOL' | 'XLM' | 'TRX' | etc.
|
|
823
|
+
* @param {number} [index=0] - Account index
|
|
824
|
+
* @param {string} [pattern] - Optional pattern override
|
|
825
|
+
* @returns {string} - Derived public address string
|
|
826
|
+
*/
|
|
827
|
+
function deriveAddress(mnemonic, walletType, coinKey, index = 0, pattern = null, language = null) {
|
|
828
|
+
if (typeof mnemonic !== 'string') {
|
|
829
|
+
throw new Error('Mnemonic must be a string');
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
const cleanWallet = walletType.toLowerCase().trim();
|
|
833
|
+
const cleanCoin = coinKey.toUpperCase().trim();
|
|
834
|
+
|
|
835
|
+
const supportedWallets = [
|
|
836
|
+
'b2wallet', 'metamask', 'trust', 'phantom', 'ledger', 'trezor',
|
|
837
|
+
'yoroi', 'electrum', 'core', 'rabby', 'daedalus', 'keplr'
|
|
838
|
+
];
|
|
839
|
+
|
|
840
|
+
if (!supportedWallets.includes(cleanWallet)) {
|
|
841
|
+
throw new Error(`Unsupported wallet profile: ${walletType}`);
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
if (cleanWallet === 'metamask' && cleanCoin !== 'ETH' && cleanCoin !== 'EVM') {
|
|
845
|
+
throw new Error(`MetaMask only supports 'ETH' / 'EVM' in standard paths. Received: ${coinKey}`);
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
// 1. Custom B2 Wallet derivation logic
|
|
849
|
+
if (cleanWallet === 'b2wallet') {
|
|
850
|
+
let coinType = 60; // Default EVM
|
|
851
|
+
switch (cleanCoin) {
|
|
852
|
+
case 'BTC':
|
|
853
|
+
case 'BITCOIN': coinType = 0; break;
|
|
854
|
+
case 'LTC':
|
|
855
|
+
case 'LITECOIN': coinType = 2; break;
|
|
856
|
+
case 'DOGE':
|
|
857
|
+
case 'DOGECOIN': coinType = 3; break;
|
|
858
|
+
case 'DASH': coinType = 5; break;
|
|
859
|
+
case 'ZEC':
|
|
860
|
+
case 'ZCASH': coinType = 133; break;
|
|
861
|
+
case 'XRP':
|
|
862
|
+
case 'RIPPLE': coinType = 144; break;
|
|
863
|
+
case 'BCH':
|
|
864
|
+
case 'BITCOINCASH': coinType = 145; break;
|
|
865
|
+
case 'XLM':
|
|
866
|
+
case 'STELLAR': coinType = 148; break;
|
|
867
|
+
case 'TRX':
|
|
868
|
+
case 'TRON': coinType = 195; break;
|
|
869
|
+
case 'ADA':
|
|
870
|
+
case 'CARDANO': coinType = 1815; break;
|
|
871
|
+
case 'DOT':
|
|
872
|
+
case 'POLKADOT': coinType = 354; break;
|
|
873
|
+
case 'KSM':
|
|
874
|
+
case 'KUSAMA': coinType = 434; break;
|
|
875
|
+
case 'ATOM':
|
|
876
|
+
case 'COSMOS': coinType = 118; break;
|
|
877
|
+
case 'OSMO':
|
|
878
|
+
case 'OSMOSIS': coinType = 118; break;
|
|
879
|
+
case 'XTZ':
|
|
880
|
+
case 'TEZOS': coinType = 1729; break;
|
|
881
|
+
case 'ALGO':
|
|
882
|
+
case 'ALGORAND': coinType = 283; break;
|
|
883
|
+
case 'NEAR': coinType = 397; break;
|
|
884
|
+
case 'SUI': coinType = 784; break;
|
|
885
|
+
case 'APT':
|
|
886
|
+
case 'APTOS': coinType = 637; break;
|
|
887
|
+
case 'KAS':
|
|
888
|
+
case 'KASPA': coinType = 111111; break;
|
|
889
|
+
case 'WAVES': coinType = 5741564; break;
|
|
890
|
+
case 'XMR':
|
|
891
|
+
case 'MONERO': coinType = 128; break;
|
|
892
|
+
case 'SCRT': coinType = 529; break;
|
|
893
|
+
case 'INJ': coinType = 60; break;
|
|
894
|
+
case 'HBAR': coinType = 3030; break;
|
|
895
|
+
case 'XEM': coinType = 43; break;
|
|
896
|
+
case 'XCH': coinType = 8444; break;
|
|
897
|
+
case 'SOL':
|
|
898
|
+
case 'SOLANA': coinType = 501; break;
|
|
899
|
+
case 'ETH':
|
|
900
|
+
case 'EVM':
|
|
901
|
+
default: coinType = 60; break;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
const masterSeed = deriveB2MasterSeed(mnemonic);
|
|
905
|
+
const privateKeyHex = deriveB2PrivateKey(masterSeed, coinType, index);
|
|
906
|
+
return deriveB2Address(privateKeyHex, cleanCoin);
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
// 2. Standard Cryptographic Derivation (MetaMask, Phantom, Yoroi, Core, Electrum, Keplr, etc.)
|
|
910
|
+
const cleanMnemonic = mnemonic.trim().toLowerCase();
|
|
911
|
+
|
|
912
|
+
// Determine path based on wallet, coin, and pattern
|
|
913
|
+
let path = `m/44'/60'/0'/0/${index}`; // Default EVM
|
|
914
|
+
let addressType = 'EVM'; // 'EVM' | 'BTC_SEGWIT' | 'BTC_LEGACY' | 'BTC_NESTED' | 'SOL_PHANTOM' | 'SOL_LEGACY' | 'TRX' | 'STellar' | 'ADA_SHELLEY' | 'ADA_BYRON' | 'XRP' | 'WAVES' | 'DOT' | 'KSM' | 'ATOM' | 'ALGO' | 'NEAR' | 'SUI' | 'APT' | 'KAS' | 'ZEC'
|
|
915
|
+
|
|
916
|
+
// Standard coin to path routing
|
|
917
|
+
if (cleanCoin === 'BTC' || cleanCoin === 'BITCOIN') {
|
|
918
|
+
if (pattern === 'legacy' || pattern === 'bip44') {
|
|
919
|
+
path = `m/44'/0'/0'/0/${index}`;
|
|
920
|
+
addressType = 'BTC_LEGACY';
|
|
921
|
+
} else if (pattern === 'nested' || pattern === 'bip49') {
|
|
922
|
+
path = `m/49'/0'/0'/0/${index}`;
|
|
923
|
+
addressType = 'BTC_NESTED';
|
|
924
|
+
} else if (pattern === 'taproot' || pattern === 'bip86') {
|
|
925
|
+
path = `m/86'/0'/0'/0/${index}`;
|
|
926
|
+
addressType = 'BTC_TAPROOT';
|
|
927
|
+
} else {
|
|
928
|
+
path = `m/84'/0'/0'/0/${index}`;
|
|
929
|
+
addressType = 'BTC_SEGWIT';
|
|
930
|
+
}
|
|
931
|
+
} else if (cleanCoin === 'LTC' || cleanCoin === 'LITECOIN') {
|
|
932
|
+
if (pattern === 'legacy' || pattern === 'bip44') {
|
|
933
|
+
path = `m/44'/2'/0'/0/${index}`;
|
|
934
|
+
addressType = 'LTC_LEGACY';
|
|
935
|
+
} else {
|
|
936
|
+
path = `m/84'/2'/0'/0/${index}`;
|
|
937
|
+
addressType = 'LTC_SEGWIT';
|
|
938
|
+
}
|
|
939
|
+
} else if (cleanCoin === 'DOGE' || cleanCoin === 'DOGECOIN') {
|
|
940
|
+
path = `m/44'/3'/0'/0/${index}`;
|
|
941
|
+
addressType = 'DOGE';
|
|
942
|
+
} else if (cleanCoin === 'TRX' || cleanCoin === 'TRON') {
|
|
943
|
+
path = `m/44'/195'/0'/0/${index}`;
|
|
944
|
+
addressType = 'TRX';
|
|
945
|
+
} else if (cleanCoin === 'SOL' || cleanCoin === 'SOLANA') {
|
|
946
|
+
if (pattern === 'sollet' || pattern === 'bip44') {
|
|
947
|
+
path = `m/44'/501'/0'/0/${index}`;
|
|
948
|
+
addressType = 'SOL_LEGACY';
|
|
949
|
+
} else {
|
|
950
|
+
path = `m/44'/501'/0'/0'`;
|
|
951
|
+
addressType = 'SOL_PHANTOM';
|
|
952
|
+
}
|
|
953
|
+
} else if (cleanCoin === 'XLM' || cleanCoin === 'STELLAR') {
|
|
954
|
+
path = `m/44'/148'/0'/0/${index}`;
|
|
955
|
+
addressType = 'STELLAR';
|
|
956
|
+
} else if (cleanCoin === 'XRP' || cleanCoin === 'RIPPLE') {
|
|
957
|
+
path = `m/44'/144'/0'/0/${index}`;
|
|
958
|
+
addressType = 'XRP';
|
|
959
|
+
} else if (cleanCoin === 'ADA' || cleanCoin === 'CARDANO') {
|
|
960
|
+
if (pattern === 'byron' || pattern === 'bip44') {
|
|
961
|
+
path = `m/44'/1815'/0'/0/${index}`;
|
|
962
|
+
addressType = 'ADA_BYRON';
|
|
963
|
+
} else {
|
|
964
|
+
path = `m/1852'/1815'/0'/0/${index}`;
|
|
965
|
+
addressType = 'ADA_SHELLEY';
|
|
966
|
+
}
|
|
967
|
+
} else if (cleanCoin === 'DOT' || cleanCoin === 'POLKADOT') {
|
|
968
|
+
path = `m/44'/354'/0'/0'/0'`;
|
|
969
|
+
addressType = 'DOT';
|
|
970
|
+
} else if (cleanCoin === 'KSM' || cleanCoin === 'KUSAMA') {
|
|
971
|
+
path = `m/44'/434'/0'/0'/0'`;
|
|
972
|
+
addressType = 'KSM';
|
|
973
|
+
} else if (cleanCoin === 'ATOM' || cleanCoin === 'COSMOS') {
|
|
974
|
+
path = `m/44'/118'/0'/0/${index}`;
|
|
975
|
+
addressType = 'ATOM';
|
|
976
|
+
} else if (cleanCoin === 'OSMO' || cleanCoin === 'OSMOSIS') {
|
|
977
|
+
path = `m/44'/118'/0'/0/${index}`;
|
|
978
|
+
addressType = 'OSMO';
|
|
979
|
+
} else if (cleanCoin === 'XTZ' || cleanCoin === 'TEZOS') {
|
|
980
|
+
path = `m/44'/1729'/0'/0/${index}`;
|
|
981
|
+
addressType = 'XTZ';
|
|
982
|
+
} else if (cleanCoin === 'ALGO' || cleanCoin === 'ALGORAND') {
|
|
983
|
+
path = `m/44'/283'/0'/0'/0'`;
|
|
984
|
+
addressType = 'ALGO';
|
|
985
|
+
} else if (cleanCoin === 'NEAR') {
|
|
986
|
+
path = `m/44'/397'/0'/0'/0'`;
|
|
987
|
+
addressType = 'NEAR';
|
|
988
|
+
} else if (cleanCoin === 'SUI') {
|
|
989
|
+
path = `m/44'/784'/0'/0'/0'`;
|
|
990
|
+
addressType = 'SUI';
|
|
991
|
+
} else if (cleanCoin === 'APT' || cleanCoin === 'APTOS') {
|
|
992
|
+
path = `m/44'/637'/0'/0'/0'`;
|
|
993
|
+
addressType = 'APT';
|
|
994
|
+
} else if (cleanCoin === 'KAS' || cleanCoin === 'KASPA') {
|
|
995
|
+
path = `m/44'/111111'/0'/0/${index}`;
|
|
996
|
+
addressType = 'KAS';
|
|
997
|
+
} else if (cleanCoin === 'WAVES') {
|
|
998
|
+
path = `m/44'/5741564'/0'/0/${index}`;
|
|
999
|
+
addressType = 'WAVES';
|
|
1000
|
+
} else if (cleanCoin === 'ZEC' || cleanCoin === 'ZCASH') {
|
|
1001
|
+
path = `m/44'/133'/0'/0/${index}`;
|
|
1002
|
+
addressType = 'ZEC';
|
|
1003
|
+
} else if (cleanCoin === 'BCH' || cleanCoin === 'BITCOINCASH') {
|
|
1004
|
+
path = `m/44'/145'/0'/0/${index}`;
|
|
1005
|
+
addressType = 'BCH';
|
|
1006
|
+
} else if (cleanCoin === 'XMR' || cleanCoin === 'MONERO') {
|
|
1007
|
+
path = `m/44'/128'/0'/0/${index}`;
|
|
1008
|
+
addressType = 'XMR';
|
|
1009
|
+
} else if (cleanCoin === 'DASH') {
|
|
1010
|
+
path = `m/44'/5'/0'/0/${index}`;
|
|
1011
|
+
addressType = 'DASH';
|
|
1012
|
+
} else if (cleanCoin === 'SCRT') {
|
|
1013
|
+
path = `m/44'/529'/0'/0/${index}`;
|
|
1014
|
+
addressType = 'SCRT';
|
|
1015
|
+
} else if (cleanCoin === 'INJ') {
|
|
1016
|
+
path = `m/44'/60'/0'/0/${index}`;
|
|
1017
|
+
addressType = 'INJ';
|
|
1018
|
+
} else if (cleanCoin === 'HBAR') {
|
|
1019
|
+
path = `m/44'/3030'/0'/0/${index}`;
|
|
1020
|
+
addressType = 'HBAR';
|
|
1021
|
+
} else if (cleanCoin === 'XEM') {
|
|
1022
|
+
path = `m/44'/43'/0'/0/${index}`;
|
|
1023
|
+
addressType = 'XEM';
|
|
1024
|
+
} else if (cleanCoin === 'XCH') {
|
|
1025
|
+
path = `m/44'/8444'/0'/0/${index}`;
|
|
1026
|
+
addressType = 'XCH';
|
|
1027
|
+
} else if (cleanCoin === 'GNOSIS') {
|
|
1028
|
+
path = `m/44'/60'/0'/0/${index}`;
|
|
1029
|
+
addressType = 'EVM';
|
|
1030
|
+
} else {
|
|
1031
|
+
// EVM fallback for Ethereum, BNB, MATIC, AVAX, ARB, OP, BASE, FTM, CRO, ONE
|
|
1032
|
+
path = `m/44'/60'/0'/0/${index}`;
|
|
1033
|
+
addressType = 'EVM';
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
// Derive HD Node
|
|
1037
|
+
let node;
|
|
1038
|
+
try {
|
|
1039
|
+
const { validateElectrumMnemonic, mnDecode } = require('./electrum-legacy');
|
|
1040
|
+
const words = cleanMnemonic.split(/\s+/);
|
|
1041
|
+
if (validateElectrumMnemonic(words)) {
|
|
1042
|
+
const hex = mnDecode(words);
|
|
1043
|
+
const seedBytes = sha256Bytes(new TextEncoder().encode(hex));
|
|
1044
|
+
node = ethers.HDNodeWallet.fromSeed(seedBytes).derivePath(path);
|
|
1045
|
+
} else {
|
|
1046
|
+
let wl;
|
|
1047
|
+
if (language) {
|
|
1048
|
+
wl = ethers.wordlists[language] || ethers.wordlists[language.toLowerCase().replace('-', '_')];
|
|
1049
|
+
}
|
|
1050
|
+
if (!wl) {
|
|
1051
|
+
wl = getEthersWordlist(cleanMnemonic);
|
|
1052
|
+
}
|
|
1053
|
+
if (!wl) {
|
|
1054
|
+
throw new Error('No matching BIP-39 wordlist detected for the given mnemonic phrase.');
|
|
1055
|
+
}
|
|
1056
|
+
node = ethers.HDNodeWallet.fromMnemonic(
|
|
1057
|
+
ethers.Mnemonic.fromPhrase(cleanMnemonic, undefined, wl),
|
|
1058
|
+
undefined,
|
|
1059
|
+
path
|
|
1060
|
+
);
|
|
1061
|
+
}
|
|
1062
|
+
} catch (err) {
|
|
1063
|
+
throw new Error(`Failed to derive HDNode for path ${path}: ${err.message}`);
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
// format based on exact address type
|
|
1067
|
+
switch (addressType) {
|
|
1068
|
+
case 'BTC_LEGACY':
|
|
1069
|
+
case 'BCH': {
|
|
1070
|
+
const hash160 = ripemd160Bytes(sha256Bytes(node.publicKey));
|
|
1071
|
+
return encodeBase58CheckWithPrefix([0x00], hash160);
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
case 'BTC_NESTED': {
|
|
1075
|
+
// Nested SegWit is P2SH(P2WPKH) -> prefix 3
|
|
1076
|
+
const keyHash = ripemd160Bytes(sha256Bytes(node.publicKey));
|
|
1077
|
+
// scriptSig: 0x16 00 14 [keyHash]
|
|
1078
|
+
const script = new Uint8Array(22);
|
|
1079
|
+
script[0] = 0x00;
|
|
1080
|
+
script[1] = 0x14;
|
|
1081
|
+
script.set(keyHash, 2);
|
|
1082
|
+
const scriptHash = ripemd160Bytes(sha256Bytes(script));
|
|
1083
|
+
return encodeBase58CheckWithPrefix([0x05], scriptHash);
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
case 'BTC_SEGWIT': {
|
|
1087
|
+
const hash160 = ripemd160Bytes(sha256Bytes(node.publicKey));
|
|
1088
|
+
return encodeBech32("bc", 0, hash160);
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
case 'LTC_LEGACY': {
|
|
1092
|
+
const hash160 = ripemd160Bytes(sha256Bytes(node.publicKey));
|
|
1093
|
+
return encodeBase58CheckWithPrefix([0x30], hash160);
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
case 'LTC_SEGWIT': {
|
|
1097
|
+
const hash160 = ripemd160Bytes(sha256Bytes(node.publicKey));
|
|
1098
|
+
return encodeBech32("ltc", 0, hash160);
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
case 'DOGE': {
|
|
1102
|
+
const hash160 = ripemd160Bytes(sha256Bytes(node.publicKey));
|
|
1103
|
+
return encodeBase58CheckWithPrefix([0x1E], hash160);
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
case 'TRX': {
|
|
1107
|
+
const uncompressed = ethers.SigningKey.computePublicKey(node.privateKey, false);
|
|
1108
|
+
const pubBytes = hexToBytes(uncompressed).subarray(1); // skip 0x04
|
|
1109
|
+
const keccakHash = keccak256Bytes(pubBytes);
|
|
1110
|
+
const addressHash = keccakHash.subarray(12);
|
|
1111
|
+
return encodeBase58CheckWithPrefix([0x41], addressHash);
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
case 'SOL_PHANTOM':
|
|
1115
|
+
case 'SOL_LEGACY': {
|
|
1116
|
+
// Standard Phantom/Sollet seed-to-address is standard Base58 of private key bytes sha256
|
|
1117
|
+
const hashBytes = sha256Bytes(node.privateKey);
|
|
1118
|
+
return encodeBase58(hashBytes);
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
case 'STELLAR': {
|
|
1122
|
+
const pubBytes = sha256Bytes(node.publicKey);
|
|
1123
|
+
const payload = new Uint8Array(35);
|
|
1124
|
+
payload[0] = 0x30; // G
|
|
1125
|
+
payload.set(pubBytes, 1);
|
|
1126
|
+
const crc = calculateStellarCRC16(payload.subarray(0, 33));
|
|
1127
|
+
payload[33] = crc & 0xFF;
|
|
1128
|
+
payload[34] = (crc >>> 8) & 0xFF;
|
|
1129
|
+
return encodeBase32(payload);
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
case 'XRP': {
|
|
1133
|
+
const hash160 = ripemd160Bytes(sha256Bytes(node.publicKey));
|
|
1134
|
+
return encodeBase58CheckWithPrefixRipple([0x00], hash160);
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
case 'ADA_SHELLEY': {
|
|
1138
|
+
const hash160 = ripemd160Bytes(sha256Bytes(node.publicKey));
|
|
1139
|
+
return encodeBech32("addr", 1, hash160);
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
case 'ADA_BYRON': {
|
|
1143
|
+
const hash160 = ripemd160Bytes(sha256Bytes(node.publicKey));
|
|
1144
|
+
return encodeBase58CheckWithPrefix([0x00], hash160);
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
case 'DOT': {
|
|
1148
|
+
const hash160 = ripemd160Bytes(sha256Bytes(node.publicKey));
|
|
1149
|
+
return encodeBase58CheckWithPrefix([0x00], hash160); // DOT Substrate
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
case 'KSM': {
|
|
1153
|
+
const hash160 = ripemd160Bytes(sha256Bytes(node.publicKey));
|
|
1154
|
+
return encodeBase58CheckWithPrefix([0x02], hash160); // Kusama starts with C/D
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
case 'ATOM': {
|
|
1158
|
+
const hash160 = ripemd160Bytes(sha256Bytes(node.publicKey));
|
|
1159
|
+
return encodeBech32("cosmos", 0, hash160);
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
case 'OSMO': {
|
|
1163
|
+
const hash160 = ripemd160Bytes(sha256Bytes(node.publicKey));
|
|
1164
|
+
return encodeBech32("osmo", 0, hash160);
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
case 'XTZ': {
|
|
1168
|
+
const hash160 = ripemd160Bytes(sha256Bytes(node.publicKey));
|
|
1169
|
+
return encodeBase58CheckWithPrefix([6, 161, 159], hash160); // tz1
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
case 'ALGO': {
|
|
1173
|
+
const pubBytes = sha256Bytes(node.publicKey);
|
|
1174
|
+
const checksum = sha256Bytes(pubBytes).subarray(28); // last 4 bytes
|
|
1175
|
+
const full = new Uint8Array(36);
|
|
1176
|
+
full.set(pubBytes);
|
|
1177
|
+
full.set(checksum, 32);
|
|
1178
|
+
return encodeBase32(full);
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
case 'WAVES': {
|
|
1182
|
+
const pubBytes = sha256Bytes(node.publicKey);
|
|
1183
|
+
const waveHash = blake2b256(keccak256Bytes(pubBytes));
|
|
1184
|
+
const payload = new Uint8Array(22);
|
|
1185
|
+
payload[0] = 0x01;
|
|
1186
|
+
payload[1] = 0x57; // 'W'
|
|
1187
|
+
payload.set(waveHash.subarray(0, 20), 2);
|
|
1188
|
+
const checksum = blake2b256(keccak256Bytes(payload)).subarray(0, 4);
|
|
1189
|
+
const full = new Uint8Array(26);
|
|
1190
|
+
full.set(payload);
|
|
1191
|
+
full.set(checksum, 22);
|
|
1192
|
+
return encodeBase58(full);
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
case 'ZEC': {
|
|
1196
|
+
const hash160 = ripemd160Bytes(sha256Bytes(node.publicKey));
|
|
1197
|
+
return encodeBase58CheckWithPrefix([0x1C, 0x82], hash160); // t1...
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
case 'BTC_TAPROOT': {
|
|
1201
|
+
const pubBytes = hexToBytes(node.publicKey);
|
|
1202
|
+
const xOnly = pubBytes.subarray(1); // pega apenas a coordenada x (32 bytes)
|
|
1203
|
+
return encodeBech32("bc", 1, xOnly, true);
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
case 'DASH': {
|
|
1207
|
+
const hash160 = ripemd160Bytes(sha256Bytes(node.publicKey));
|
|
1208
|
+
return encodeBase58CheckWithPrefix([0x4c], hash160);
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
case 'SCRT': {
|
|
1212
|
+
const hash160 = ripemd160Bytes(sha256Bytes(node.publicKey));
|
|
1213
|
+
return encodeBech32("secret", 0, hash160);
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
case 'INJ': {
|
|
1217
|
+
const uncompressed = ethers.SigningKey.computePublicKey(node.privateKey, false);
|
|
1218
|
+
const pubBytes = hexToBytes(uncompressed).subarray(1); // pula o byte 0x04
|
|
1219
|
+
const keccakHash = keccak256Bytes(pubBytes);
|
|
1220
|
+
const addressHash = keccakHash.subarray(12);
|
|
1221
|
+
return encodeBech32("inj", 0, addressHash);
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
case 'HBAR': {
|
|
1225
|
+
return node.publicKey.toLowerCase();
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
case 'XEM': {
|
|
1229
|
+
const pubBytes = hexToBytes(node.publicKey);
|
|
1230
|
+
const hashed = ripemd160Bytes(sha256Bytes(pubBytes));
|
|
1231
|
+
const payload = new Uint8Array(21);
|
|
1232
|
+
payload[0] = 0x68;
|
|
1233
|
+
payload.set(hashed, 1);
|
|
1234
|
+
const checksum = sha256Bytes(payload).subarray(0, 4);
|
|
1235
|
+
const full = new Uint8Array(25);
|
|
1236
|
+
full.set(payload);
|
|
1237
|
+
full.set(checksum, 21);
|
|
1238
|
+
return encodeBase32(full);
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
case 'XCH': {
|
|
1242
|
+
const hash160 = ripemd160Bytes(sha256Bytes(node.publicKey));
|
|
1243
|
+
return encodeBech32("xch", 0, hash160, true);
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
case 'KAS': {
|
|
1247
|
+
const hash160 = ripemd160Bytes(sha256Bytes(node.publicKey));
|
|
1248
|
+
return 'kaspa:' + encodeBech32("kaspa", 0, hash160).replace('kaspa1', '');
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
case 'NEAR':
|
|
1252
|
+
case 'SUI':
|
|
1253
|
+
case 'APT':
|
|
1254
|
+
case 'XMR': {
|
|
1255
|
+
// Representação em hex para as redes modernas
|
|
1256
|
+
const hashBytes = sha256Bytes(node.publicKey);
|
|
1257
|
+
return '0x' + Array.from(hashBytes).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
case 'EVM':
|
|
1261
|
+
default:
|
|
1262
|
+
return node.address;
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
module.exports = {
|
|
1267
|
+
// Low level crypto for testing/parity check
|
|
1268
|
+
keccak256,
|
|
1269
|
+
blake2b256,
|
|
1270
|
+
encodeBase58,
|
|
1271
|
+
encodeBase58Ripple,
|
|
1272
|
+
encodeBase32,
|
|
1273
|
+
encodeBech32,
|
|
1274
|
+
calculateStellarCRC16,
|
|
1275
|
+
deriveB2MasterSeed,
|
|
1276
|
+
deriveB2PrivateKey,
|
|
1277
|
+
deriveB2Address,
|
|
1278
|
+
|
|
1279
|
+
// Public API
|
|
1280
|
+
deriveAddress,
|
|
1281
|
+
getEthersWordlist
|
|
1282
|
+
};
|