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.
@@ -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
+ };