@@ -6,13 +6,56 @@ import chalk from "chalk";
66import { HttpsProxyAgent } from "https-proxy-agent" ;
77import { SocksProxyAgent } from "socks-proxy-agent" ;
88import { fileURLToPath } from "url" ;
9+ import crypto from "crypto" ;
910
1011dotenv . config ( ) ;
1112
1213// Define __dirname equivalent for ES Modules
1314const __filename = fileURLToPath ( import . meta. url ) ;
1415const __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.
1861const RPCS = [
@@ -26,9 +69,17 @@ const keysFile = "key.txt";
2669let 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 = [ ] ;
3074let 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
3384function 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() {
192248setInterval ( 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
196252function 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