crypto-wallet-xpub 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,136 @@
1
+ /**
2
+ * BEP20 Wallet Generator
3
+ *
4
+ * Generates BEP20 (Binance Smart Chain) wallet addresses from Account Extended Public Key (XPUB).
5
+ *
6
+ * @module bep20-wallet
7
+ */
8
+
9
+ const { HDNodeWallet } = require('ethers');
10
+
11
+ /**
12
+ * BEP20 Wallet Class
13
+ *
14
+ * @class BEP20Wallet
15
+ * @example
16
+ * const { BEP20Wallet } = require('crypto-wallet-xpub');
17
+ *
18
+ * const xpub = "xpub6Ce5wAYj54UjjC6T83tSoriMdebVzDJfNjB7ocoY9DJshVZsbciCgvLaKLJp38Zh7x8v2222Fob3Shqcum7g3Kg6Ja7igUo3LdrzNb9E93t";
19
+ * const wallet = new BEP20Wallet(xpub);
20
+ *
21
+ * // Get single address
22
+ * const address = wallet.getAddress(0);
23
+ * console.log(address); // 0x9894A0B1863382F74DCe886C6Bf6817C604BEE52
24
+ *
25
+ * // Get multiple addresses
26
+ * const addresses = wallet.getAddresses(0, 4);
27
+ * console.log(addresses);
28
+ */
29
+ class BEP20Wallet {
30
+ /**
31
+ * Creates a new BEP20 wallet instance
32
+ *
33
+ * @param {string} xpub - Account Extended Public Key (XPUB) derived from path m/44'/60'/0'
34
+ * @throws {Error} If XPUB is not provided or invalid
35
+ *
36
+ * @example
37
+ * const wallet = new BEP20Wallet("xpub6Ce5wAYj54Ujj...");
38
+ */
39
+ constructor(xpub) {
40
+ if (!xpub) {
41
+ throw new Error('XPUB is required');
42
+ }
43
+
44
+ if (typeof xpub !== 'string') {
45
+ throw new Error('XPUB must be a string');
46
+ }
47
+
48
+ if (!xpub.startsWith('xpub')) {
49
+ throw new Error('Invalid XPUB format. Must start with "xpub"');
50
+ }
51
+
52
+ try {
53
+ this.xpub = xpub;
54
+ this.root = HDNodeWallet.fromExtendedKey(xpub);
55
+ } catch (error) {
56
+ throw new Error(`Invalid XPUB: ${error.message}`);
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Get a single BEP20 address at specified index
62
+ *
63
+ * @param {number} index - Derivation index (0, 1, 2, ...)
64
+ * @returns {string} BEP20 wallet address (0x...)
65
+ * @throws {Error} If index is invalid
66
+ *
67
+ * @example
68
+ * const address = wallet.getAddress(0);
69
+ * console.log(address); // "0x9894A0B1863382F74DCe886C6Bf6817C604BEE52"
70
+ */
71
+ getAddress(index) {
72
+ if (typeof index !== 'number' || index < 0 || !Number.isInteger(index)) {
73
+ throw new Error('Index must be a non-negative integer');
74
+ }
75
+
76
+ try {
77
+ const child = this.root.derivePath(`0/${index}`);
78
+ return child.address;
79
+ } catch (error) {
80
+ throw new Error(`Failed to derive address at index ${index}: ${error.message}`);
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Get multiple BEP20 addresses in a range
86
+ *
87
+ * @param {number} start - Start index (inclusive)
88
+ * @param {number} end - End index (inclusive)
89
+ * @returns {Array<{index: number, address: string}>} Array of address objects
90
+ * @throws {Error} If range is invalid
91
+ *
92
+ * @example
93
+ * const addresses = wallet.getAddresses(0, 4);
94
+ * // [
95
+ * // { index: 0, address: "0x9894A0B1863382F74DCe886C6Bf6817C604BEE52" },
96
+ * // { index: 1, address: "0x50EC5410B937438C49fdFf2981166064E4dF95cB" },
97
+ * // ...
98
+ * // ]
99
+ */
100
+ getAddresses(start, end) {
101
+ if (typeof start !== 'number' || typeof end !== 'number') {
102
+ throw new Error('Start and end must be numbers');
103
+ }
104
+
105
+ if (start < 0 || end < 0 || !Number.isInteger(start) || !Number.isInteger(end)) {
106
+ throw new Error('Start and end must be non-negative integers');
107
+ }
108
+
109
+ if (start > end) {
110
+ throw new Error('Start index must be less than or equal to end index');
111
+ }
112
+
113
+ const addresses = [];
114
+ for (let i = start; i <= end; i++) {
115
+ addresses.push({
116
+ index: i,
117
+ address: this.getAddress(i)
118
+ });
119
+ }
120
+ return addresses;
121
+ }
122
+
123
+ /**
124
+ * Get the XPUB used by this wallet
125
+ *
126
+ * @returns {string} The Account Extended Public Key
127
+ *
128
+ * @example
129
+ * const xpub = wallet.getXPUB();
130
+ */
131
+ getXPUB() {
132
+ return this.xpub;
133
+ }
134
+ }
135
+
136
+ module.exports = { BEP20Wallet };
package/index.js ADDED
@@ -0,0 +1,14 @@
1
+ /**
2
+ * crypto-wallet-xpub
3
+ * Generate BEP20 and TRC20 addresses from Account Extended Public Keys
4
+ *
5
+ * @module crypto-wallet-xpub
6
+ */
7
+
8
+ const { BEP20Wallet } = require('./bep20-wallet');
9
+ const { TRC20Wallet } = require('./trc20-wallet');
10
+
11
+ module.exports = {
12
+ BEP20Wallet,
13
+ TRC20Wallet
14
+ };
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "crypto-wallet-xpub",
3
+ "version": "1.0.0",
4
+ "description": "Generate BEP20 (BSC) and TRC20 (TRON) wallet addresses from Account Extended Public Keys (XPUB)",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "node examples/bep20-example.js && node examples/trc20-example.js"
8
+ },
9
+ "keywords": [
10
+ "crypto",
11
+ "wallet",
12
+ "bep20",
13
+ "trc20",
14
+ "bsc",
15
+ "binance-smart-chain",
16
+ "tron",
17
+ "xpub",
18
+ "bip32",
19
+ "bip44",
20
+ "hdwallet",
21
+ "hierarchical-deterministic"
22
+ ],
23
+ "author": "offici5l>",
24
+ "license": "MIT",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "https://github.com/offici5l/crypto-wallet-xpub.git"
28
+ },
29
+ "bugs": {
30
+ "url": "https://github.com/offici5l/crypto-wallet-xpub/issues"
31
+ },
32
+ "homepage": "https://github.com/offici5l/crypto-wallet-xpub#readme",
33
+ "dependencies": {
34
+ "ethers": "^6.9.0",
35
+ "hdkey": "^2.1.0",
36
+ "secp256k1": "^5.0.0",
37
+ "keccak": "^3.0.4"
38
+ },
39
+ "engines": {
40
+ "node": ">=14.0.0"
41
+ }
42
+ }
@@ -0,0 +1,199 @@
1
+ /**
2
+ * TRC20 Wallet Generator
3
+ *
4
+ * Generates TRC20 (TRON) wallet addresses from Account Extended Public Key (XPUB).
5
+ *
6
+ * @module trc20-wallet
7
+ */
8
+
9
+ const HDKey = require('hdkey');
10
+ const secp256k1 = require('secp256k1');
11
+ const createKeccakHash = require('keccak');
12
+ const crypto = require('crypto');
13
+
14
+ /**
15
+ * TRC20 Wallet Class
16
+ *
17
+ * @class TRC20Wallet
18
+ * @example
19
+ * const { TRC20Wallet } = require('crypto-wallet-xpub');
20
+ *
21
+ * const xpub = "xpub6C5KYvkMHmGWqx3yDSqiwcrgQbhxzQFUFPURg7Tvw3dELngbRd1sAuajPT1NsCfohgJHojoBS7XaQgBCMj2eJmX9xYAD9TzCHPXhkrw2PMy";
22
+ * const wallet = new TRC20Wallet(xpub);
23
+ *
24
+ * // Get single address
25
+ * const address = wallet.getAddress(0);
26
+ * console.log(address); // TFs5Wcs68CrmDG113bTJfKEUHvKUf9Vz6a
27
+ *
28
+ * // Get multiple addresses
29
+ * const addresses = wallet.getAddresses(0, 4);
30
+ * console.log(addresses);
31
+ */
32
+ class TRC20Wallet {
33
+ /**
34
+ * Creates a new TRC20 wallet instance
35
+ *
36
+ * @param {string} xpub - Account Extended Public Key (XPUB) derived from path m/44'/195'/0'
37
+ * @throws {Error} If XPUB is not provided or invalid
38
+ *
39
+ * @example
40
+ * const wallet = new TRC20Wallet("xpub6C5KYvkMHmGW...");
41
+ */
42
+ constructor(xpub) {
43
+ if (!xpub) {
44
+ throw new Error('XPUB is required');
45
+ }
46
+
47
+ if (typeof xpub !== 'string') {
48
+ throw new Error('XPUB must be a string');
49
+ }
50
+
51
+ if (!xpub.startsWith('xpub')) {
52
+ throw new Error('Invalid XPUB format. Must start with "xpub"');
53
+ }
54
+
55
+ try {
56
+ this.xpub = xpub;
57
+ this.master = HDKey.fromExtendedKey(xpub);
58
+ this.changeNode = this.master.deriveChild(0);
59
+ } catch (error) {
60
+ throw new Error(`Invalid XPUB: ${error.message}`);
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Encode address bytes to Base58Check format (TRON address format)
66
+ *
67
+ * @private
68
+ * @param {Buffer} payload - Address bytes to encode
69
+ * @returns {string} Base58Check encoded address
70
+ */
71
+ base58CheckEncode(payload) {
72
+ const buffer = Buffer.isBuffer(payload) ? payload : Buffer.from(payload);
73
+ const hash1 = crypto.createHash("sha256").update(buffer).digest();
74
+ const hash2 = crypto.createHash("sha256").update(hash1).digest();
75
+ const checksum = hash2.slice(0, 4);
76
+ const data = Buffer.concat([buffer, checksum]);
77
+
78
+ const alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
79
+ let num = BigInt("0x" + data.toString("hex"));
80
+ let encoded = "";
81
+
82
+ while (num > 0n) {
83
+ const mod = Number(num % 58n);
84
+ encoded = alphabet[mod] + encoded;
85
+ num = num / 58n;
86
+ }
87
+
88
+ for (const byte of data) {
89
+ if (byte !== 0) break;
90
+ encoded = "1" + encoded;
91
+ }
92
+
93
+ return encoded;
94
+ }
95
+
96
+ /**
97
+ * Generate TRON address from compressed public key
98
+ *
99
+ * @private
100
+ * @param {Buffer} compressedPubKey - Compressed public key (33 bytes)
101
+ * @returns {string} TRON address (T...)
102
+ */
103
+ generateTronAddress(compressedPubKey) {
104
+ // Convert compressed public key to uncompressed format
105
+ const uncompressedPubKey = secp256k1.publicKeyConvert(
106
+ Buffer.isBuffer(compressedPubKey) ? compressedPubKey : Buffer.from(compressedPubKey),
107
+ false
108
+ );
109
+
110
+ const uncompressedBuffer = Buffer.from(uncompressedPubKey);
111
+
112
+ // Apply Keccak256 hash
113
+ const hash = createKeccakHash('keccak256').update(uncompressedBuffer.slice(1)).digest();
114
+
115
+ // Take last 20 bytes and add TRON prefix (0x41)
116
+ const addressBytes = hash.slice(-20);
117
+ const addressHex = "41" + addressBytes.toString('hex');
118
+
119
+ // Encode to Base58Check
120
+ return this.base58CheckEncode(Buffer.from(addressHex, "hex"));
121
+ }
122
+
123
+ /**
124
+ * Get a single TRC20 address at specified index
125
+ *
126
+ * @param {number} index - Derivation index (0, 1, 2, ...)
127
+ * @returns {string} TRC20 wallet address (T...)
128
+ * @throws {Error} If index is invalid
129
+ *
130
+ * @example
131
+ * const address = wallet.getAddress(0);
132
+ * console.log(address); // "TFs5Wcs68CrmDG113bTJfKEUHvKUf9Vz6a"
133
+ */
134
+ getAddress(index) {
135
+ if (typeof index !== 'number' || index < 0 || !Number.isInteger(index)) {
136
+ throw new Error('Index must be a non-negative integer');
137
+ }
138
+
139
+ try {
140
+ const node = this.changeNode.deriveChild(index);
141
+ return this.generateTronAddress(node.publicKey);
142
+ } catch (error) {
143
+ throw new Error(`Failed to derive address at index ${index}: ${error.message}`);
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Get multiple TRC20 addresses in a range
149
+ *
150
+ * @param {number} start - Start index (inclusive)
151
+ * @param {number} end - End index (inclusive)
152
+ * @returns {Array<{index: number, address: string}>} Array of address objects
153
+ * @throws {Error} If range is invalid
154
+ *
155
+ * @example
156
+ * const addresses = wallet.getAddresses(0, 4);
157
+ * // [
158
+ * // { index: 0, address: "TFs5Wcs68CrmDG113bTJfKEUHvKUf9Vz6a" },
159
+ * // { index: 1, address: "TEFosF8dnrgLYx4gAwYVq1sDpRTNUQ9br5" },
160
+ * // ...
161
+ * // ]
162
+ */
163
+ getAddresses(start, end) {
164
+ if (typeof start !== 'number' || typeof end !== 'number') {
165
+ throw new Error('Start and end must be numbers');
166
+ }
167
+
168
+ if (start < 0 || end < 0 || !Number.isInteger(start) || !Number.isInteger(end)) {
169
+ throw new Error('Start and end must be non-negative integers');
170
+ }
171
+
172
+ if (start > end) {
173
+ throw new Error('Start index must be less than or equal to end index');
174
+ }
175
+
176
+ const addresses = [];
177
+ for (let i = start; i <= end; i++) {
178
+ addresses.push({
179
+ index: i,
180
+ address: this.getAddress(i)
181
+ });
182
+ }
183
+ return addresses;
184
+ }
185
+
186
+ /**
187
+ * Get the XPUB used by this wallet
188
+ *
189
+ * @returns {string} The Account Extended Public Key
190
+ *
191
+ * @example
192
+ * const xpub = wallet.getXPUB();
193
+ */
194
+ getXPUB() {
195
+ return this.xpub;
196
+ }
197
+ }
198
+
199
+ module.exports = { TRC20Wallet };