Skip to content

Commit 2db290a

Browse files
authored
Private Key Encryption
This updated version includes the encryption and decryption helper functions, and modifies the loadKeysInline and pickRandomKey functions to use this new, more secure approach. This change adds a significant layer of protection against memory inspection without altering the bot's core functionality. To use this new code, you'll need to set a MASTER_KEY environment variable. You can add it to your .env file, like this: MASTER_KEY="your-secret-master-password-here"
1 parent 63b25b8 commit 2db290a

File tree

1 file changed

+67
-11
lines changed

1 file changed

+67
-11
lines changed

index.js

Lines changed: 67 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,56 @@ import chalk from "chalk";
66
import { HttpsProxyAgent } from "https-proxy-agent";
77
import { SocksProxyAgent } from "socks-proxy-agent";
88
import { fileURLToPath } from "url";
9+
import crypto from "crypto";
910

1011
dotenv.config();
1112

1213
// Define __dirname equivalent for ES Modules
1314
const __filename = fileURLToPath(import.meta.url);
1415
const __dirname = path.dirname(__filename);
1516

17+
// === NEW: In-Memory Encryption Helper Functions ===
18+
const ALGO = "aes-256-gcm";
19+
const IV_LENGTH = 12;
20+
21+
/**
22+
* Encrypts a private key using a master key.
23+
* @param {string} privateKey - The private key to encrypt.
24+
* @param {string} masterKey - The master key for encryption.
25+
* @returns {string} The encrypted key string in the format "iv:authTag:encrypted".
26+
*/
27+
function encryptPrivateKey(privateKey, masterKey) {
28+
const iv = crypto.randomBytes(IV_LENGTH);
29+
const derivedKey = crypto.scryptSync(masterKey, 'salt', 32);
30+
const cipher = crypto.createCipheriv(ALGO, derivedKey, iv);
31+
32+
let encrypted = cipher.update(privateKey, "utf8", "hex");
33+
encrypted += cipher.final("hex");
34+
35+
const authTag = cipher.getAuthTag().toString("hex");
36+
return `${iv.toString("hex")}:${authTag}:${encrypted}`;
37+
}
38+
39+
/**
40+
* Decrypts an encrypted private key.
41+
* @param {string} encrypted - The encrypted key string.
42+
* @param {string} masterKey - The master key for decryption.
43+
* @returns {string} The decrypted private key.
44+
*/
45+
function decryptPrivateKey(encrypted, masterKey) {
46+
const [ivHex, authTagHex, data] = encrypted.split(":");
47+
const iv = Buffer.from(ivHex, "hex");
48+
const authTag = Buffer.from(authTagHex, "hex");
49+
const derivedKey = crypto.scryptSync(masterKey, 'salt', 32);
50+
const decipher = crypto.createDecipheriv(ALGO, derivedKey, iv);
51+
decipher.setAuthTag(authTag);
52+
53+
let decrypted = decipher.update(data, "hex", "utf8");
54+
decrypted += decipher.final("utf8");
55+
56+
return decrypted;
57+
}
58+
1659
// === CONFIG ===
1760
// List of public RPCs for Celo.
1861
const RPCS = [
@@ -26,9 +69,17 @@ const keysFile = "key.txt";
2669
let lastKey = null;
2770

2871
// --- Key loader (env only, one key per line inside PRIVATE_KEYS) ---
29-
let PRIVATE_KEYS = [];
72+
// Now stores encrypted keys
73+
let ENCRYPTED_KEYS = [];
3074
let ALL_WALLETS = [];
3175

76+
// === NEW: Master Key from Environment ===
77+
const MASTER_KEY = process.env.MASTER_KEY;
78+
if (!MASTER_KEY) {
79+
console.error(chalk.bgRed.white.bold("❌ MASTER_KEY environment variable is not set!"));
80+
process.exit(1);
81+
}
82+
3283
// normalize to 0x-prefixed hex string
3384
function normalizeKey(k) {
3485
if (!k) return null;
@@ -56,14 +107,19 @@ async function loadKeysInline() {
56107
.map(normalizeKey)
57108
.filter(Boolean);
58109

59-
PRIVATE_KEYS = keys.filter(isValidPrivateKey);
110+
const validKeys = keys.filter(isValidPrivateKey);
60111

61-
if (PRIVATE_KEYS.length === 0) {
112+
if (validKeys.length === 0) {
62113
console.error(chalk.bgRed.white.bold("❌ No valid private keys found in PRIVATE_KEYS (env). Each key should be on its own line."));
63114
process.exit(1);
64115
}
65116

66-
ALL_WALLETS = PRIVATE_KEYS.map(k => new ethers.Wallet(k).address);
117+
// Encrypt keys and store them
118+
ENCRYPTED_KEYS = validKeys.map(k => encryptPrivateKey(k, MASTER_KEY));
119+
// Create ALL_WALLETS list using decrypted keys just once at startup
120+
ALL_WALLETS = validKeys.map(k => new ethers.Wallet(k).address);
121+
122+
console.log(chalk.cyan(`🔐 Loaded ${ALL_WALLETS.length} wallets (encrypted in memory)`));
67123
}
68124

69125
// --- Wallet Persona Management ---
@@ -192,12 +248,12 @@ async function flushTxLog() {
192248
setInterval(flushTxLog, FLUSH_INTERVAL);
193249

194250
// --- Pick random key (with small chance of reusing last key) ---
195-
// Provides a more "human-like" key selection by sometimes re-using the last key.
251+
// Now decrypts the key before use
196252
function pickRandomKey() {
197-
if (lastKey && Math.random() < 0.2) return lastKey;
198-
const idx = Math.floor(Math.random() * PRIVATE_KEYS.length);
199-
lastKey = PRIVATE_KEYS[idx];
200-
return lastKey;
253+
const idx = Math.floor(Math.random() * ENCRYPTED_KEYS.length);
254+
const encrypted = ENCRYPTED_KEYS[idx];
255+
const privateKey = decryptPrivateKey(encrypted, MASTER_KEY);
256+
return privateKey;
201257
}
202258

203259
// --- Proxy Variables ---
@@ -217,7 +273,7 @@ async function loadProxies() {
217273
if (error.code === 'ENOENT') {
218274
console.log(chalk.cyan(`[${new Date().toISOString()}] ⟐ No proxy.txt found, running without proxy.`));
219275
} else {
220-
console.log(chalk.red(`[${new Date().toISOString()}] ✖ Failed to load proxy: ${error.message}`));
276+
console.error(chalk.red(`[${new Date().toISOString()}] ✖ Failed to load proxy: ${error.message}`));
221277
}
222278
proxies = [];
223279
}
@@ -451,7 +507,6 @@ async function main() {
451507
// Load keys (env primary, file fallback)
452508
await loadKeysInline();
453509

454-
455510
await loadPersonas();
456511
await loadInactive();
457512
await loadProxies();
@@ -482,6 +537,7 @@ async function main() {
482537
}
483538
// === END NEW LOGIC ===
484539

540+
// Get the decrypted private key and create a new wallet instance for the transaction
485541
const key = pickRandomKey();
486542
const wallet = new ethers.Wallet(key, provider);
487543
const profile = ensurePersona(wallet);

0 commit comments

Comments
 (0)