@@ -69,7 +69,7 @@ process.on("unhandledRejection", (reason, promise) => {
6969 console . error ( chalk . bgRed . white . bold ( "[Unhandled Rejection]" ) , reason ) ;
7070} ) ;
7171
72- // === NEW : In-Memory Encryption Helper Functions ===
72+ // === FIXED : In-Memory Encryption Helper Functions ===
7373const ALGO = "aes-256-gcm" ;
7474const IV_LENGTH = 12 ;
7575
@@ -87,40 +87,34 @@ if (!MASTER_KEY) {
8787
8888const ALL_MASTER_KEYS = [ MASTER_KEY , ...BACKUP_KEYS ] ;
8989
90- // --- Session Salt for Stronger Key Derivation ---
91- let sessionSalt = crypto . randomBytes ( 16 ) ;
92-
93- // Rotate session salt hourly
94- setInterval ( ( ) => {
95- sessionSalt = crypto . randomBytes ( 16 ) ;
96- console . log ( chalk . cyan ( "🔑 Rotated session salt for key derivation" ) ) ;
97- } , 60 * 60 * 1000 ) ;
98-
9990/**
100- * Derives a cryptographic key from a master key and a session salt.
91+ * Derives a cryptographic key from a master key and a salt.
10192 * @param {string } masterKey - The master key for key derivation.
93+ * @param {Buffer } salt - The salt for key derivation.
10294 * @returns {Buffer } The derived key as a Buffer.
10395 */
104- function deriveKey ( masterKey ) {
105- return crypto . scryptSync ( masterKey , sessionSalt , 32 ) ;
96+ function deriveKey ( masterKey , salt ) {
97+ return crypto . scryptSync ( masterKey , salt , 32 ) ;
10698}
10799
108100/**
109101 * Encrypts a private key using a master key.
110102 * @param {string } privateKey - The private key to encrypt.
111103 * @param {string } masterKey - The master key for encryption.
112- * @returns {string } The encrypted key string in the format "iv:authTag:encrypted".
104+ * @returns {string } The encrypted key string in the format "salt: iv:authTag:encrypted".
113105 */
114106function encryptPrivateKey ( privateKey , masterKey ) {
107+ const salt = crypto . randomBytes ( 16 ) ; // Generate unique salt for this encryption
115108 const iv = crypto . randomBytes ( IV_LENGTH ) ;
116- const derivedKey = deriveKey ( masterKey ) ;
109+ const derivedKey = deriveKey ( masterKey , salt ) ;
117110 const cipher = crypto . createCipheriv ( ALGO , derivedKey , iv ) ;
118111
119112 let encrypted = cipher . update ( privateKey , "utf8" , "hex" ) ;
120113 encrypted += cipher . final ( "hex" ) ;
121114
122115 const authTag = cipher . getAuthTag ( ) . toString ( "hex" ) ;
123- return `${ iv . toString ( "hex" ) } :${ authTag } :${ encrypted } ` ;
116+ // IMPORTANT: Store salt with the encrypted data
117+ return `${ salt . toString ( "hex" ) } :${ iv . toString ( "hex" ) } :${ authTag } :${ encrypted } ` ;
124118}
125119
126120/**
@@ -130,10 +124,11 @@ function encryptPrivateKey(privateKey, masterKey) {
130124 * @returns {string } The decrypted private key.
131125 */
132126function decryptPrivateKey ( encrypted , masterKey ) {
133- const [ ivHex , authTagHex , data ] = encrypted . split ( ":" ) ;
127+ const [ saltHex , ivHex , authTagHex , data ] = encrypted . split ( ":" ) ;
128+ const salt = Buffer . from ( saltHex , "hex" ) ; // Extract salt from encrypted data
134129 const iv = Buffer . from ( ivHex , "hex" ) ;
135130 const authTag = Buffer . from ( authTagHex , "hex" ) ;
136- const derivedKey = deriveKey ( masterKey ) ;
131+ const derivedKey = deriveKey ( masterKey , salt ) ; // Use the original salt
137132 const decipher = crypto . createDecipheriv ( ALGO , derivedKey , iv ) ;
138133 decipher . setAuthTag ( authTag ) ;
139134
@@ -233,12 +228,12 @@ async function loadKeysInline() {
233228 process . exit ( 1 ) ;
234229 }
235230
236- // Encrypt keys and store them
231+ // Encrypt keys and store them (each with its own salt)
237232 ENCRYPTED_KEYS = validKeys . map ( k => encryptPrivateKey ( k , MASTER_KEY ) ) ;
238233 // Create ALL_WALLETS list using decrypted keys just once at startup
239234 ALL_WALLETS = validKeys . map ( k => new ethers . Wallet ( k ) . address ) ;
240235
241- console . log ( chalk . cyan ( `🔐 Loaded ${ ALL_WALLETS . length } wallets (encrypted in memory)` ) ) ;
236+ console . log ( chalk . cyan ( `🔐 Loaded ${ ALL_WALLETS . length } wallets (encrypted in memory with individual salts )` ) ) ;
242237}
243238
244239// === NEW: Device Agent Generation ===
@@ -434,7 +429,8 @@ setInterval(async () => {
434429} , 5 * 60 * 1000 ) ; // 5 minutes
435430
436431// --- Pick random key (with small chance of reusing last key) ---
437- // Now decrypts the key before use
432+ // Now pickRandomKey() will work correctly because each encrypted key
433+ // includes its own salt for decryption
438434function pickRandomKey ( ) {
439435 const idx = Math . floor ( Math . random ( ) * ENCRYPTED_KEYS . length ) ;
440436 const encrypted = ENCRYPTED_KEYS [ idx ] ;
@@ -525,9 +521,10 @@ function getProvider(rpcUrl, agent, userAgent) {
525521 * @param {string } rpcUrl - The RPC endpoint URL
526522 * @returns {Promise<bigint|null> } The gas price in wei, or null if failed
527523 */
528- async function getGasPriceFromRpc ( rpcUrl ) {
524+ async function getGasPriceFromRpc ( rpcUrl , proxyUrl = null , userAgent = null ) {
529525 try {
530- const provider = getProvider ( rpcUrl , null , null ) ;
526+ const agent = proxyUrl ? createAgent ( proxyUrl ) : null ;
527+ const provider = getProvider ( rpcUrl , agent , userAgent ) ;
531528 const feeData = await provider . getFeeData ( ) ;
532529
533530 // Prioritize EIP-1559 maxFeePerGas, fallback to legacy gasPrice
@@ -542,13 +539,13 @@ async function getGasPriceFromRpc(rpcUrl) {
542539 * Gets average gas price from multiple RPCs
543540 * @returns {Promise<{avgGasPriceGwei: number, gasPricesGwei: number[]}|null> } Average gas price and individual prices
544541 */
545- async function getAverageGasPrice ( verbose = true ) {
542+ async function getAverageGasPrice ( verbose = true , proxyUrl = null , userAgent = null ) {
546543 if ( verbose ) {
547544 console . log ( chalk . cyan ( "🔍 Checking gas prices from multiple RPCs..." ) ) ;
548545 }
549546
550547 // Fetch gas prices from all RPCs concurrently
551- const gasPricePromises = GAS_RPC_URLS . map ( url => getGasPriceFromRpc ( url ) ) ;
548+ const gasPricePromises = GAS_RPC_URLS . map ( url => getGasPriceFromRpc ( url , proxyUrl , userAgent ) ) ;
552549 const gasPrices = await Promise . all ( gasPricePromises ) ;
553550
554551 // Filter out null values
@@ -613,9 +610,11 @@ async function simulateMicroBehaviors() {
613610 * Implements random hesitation when gas prices spike (simulating human behavior)
614611 * @param {number } avgGasPriceGwei - Average gas price in Gwei
615612 * @param {number[] } gasPricesGwei - Individual gas prices from different RPCs
613+ * @param {string } proxyUrl - The proxy URL being used
614+ * @param {string } userAgent - The user agent being used
616615 * @returns {Promise<boolean> } True if should proceed with transaction
617616 */
618- async function shouldProceedWithGasSpike ( avgGasPriceGwei , gasPricesGwei ) {
617+ async function shouldProceedWithGasSpike ( avgGasPriceGwei , gasPricesGwei , proxyUrl , userAgent ) {
619618 // Calculate standard deviation to detect price volatility
620619 const variance = gasPricesGwei . reduce ( ( acc , price ) => acc + Math . pow ( price - avgGasPriceGwei , 2 ) , 0 ) / gasPricesGwei . length ;
621620 const stdDev = Math . sqrt ( variance ) ;
@@ -634,7 +633,7 @@ async function shouldProceedWithGasSpike(avgGasPriceGwei, gasPricesGwei) {
634633 await new Promise ( resolve => setTimeout ( resolve , hesitationTime * 1000 ) ) ;
635634
636635 // After hesitation, check gas prices again (less verbose)
637- const updatedGasData = await getAverageGasPrice ( false ) ;
636+ const updatedGasData = await getAverageGasPrice ( false , proxyUrl , userAgent ) ;
638637 if ( updatedGasData && isGasPriceAcceptable ( updatedGasData . avgGasPriceGwei ) ) {
639638 console . log ( chalk . green ( "✅ Gas prices improved after hesitation" ) ) ;
640639 return true ;
@@ -649,7 +648,7 @@ async function shouldProceedWithGasSpike(avgGasPriceGwei, gasPricesGwei) {
649648
650649/**
651650 * Attempts to connect to an RPC endpoint.
652- * @returns {Promise<{provider: ethers.JsonRpcProvider, url: string}|null> } The working provider and its URL, or null if all fail.
651+ * @returns {Promise<{provider: ethers.JsonRpcProvider, url: string, proxyUrl: string, userAgent: string }|null> } The working provider and its URL, or null if all fail.
653652 */
654653async function tryProviders ( profile ) {
655654 console . log ( chalk . hex ( "#00FFFF" ) . bold ( "🔍 Searching for a working RPC endpoint..." ) ) ;
@@ -665,7 +664,7 @@ async function tryProviders(profile) {
665664 new Promise ( ( _ , reject ) => setTimeout ( ( ) => reject ( new Error ( "Timeout" ) ) , 5000 ) )
666665 ] ) ;
667666 // Only log successful connections to reduce verbosity
668- return { provider, url } ;
667+ return { provider, url, proxyUrl , userAgent } ;
669668 } catch ( e ) {
670669 // Silently ignore failed connections to reduce verbosity
671670 }
@@ -675,7 +674,7 @@ async function tryProviders(profile) {
675674
676675/**
677676 * Iterates through the list of RPCs and returns the first one that successfully connects.
678- * @returns {Promise<{provider: ethers.JsonRpcProvider, url: string}> } The working provider and its URL.
677+ * @returns {Promise<{provider: ethers.JsonRpcProvider, url: string, proxyUrl: string, userAgent: string }> } The working provider and its URL.
679678 */
680679async function getWorkingProvider ( profile ) {
681680 return await tryProviders ( profile ) ;
@@ -716,9 +715,11 @@ function checkActive(wallet, profile) {
716715 * @param {ethers.JsonRpcProvider } provider - The RPC provider.
717716 * @param {object } profile - The wallet's persona profile.
718717 * @param {string } url - The URL of the RPC being used.
718+ * @param {string } proxyUrl - The proxy URL being used.
719+ * @param {string } userAgent - The user agent being used.
719720 * @throws {Error } If the transaction fails.
720721 */
721- async function sendTx ( wallet , provider , profile , url ) {
722+ async function sendTx ( wallet , provider , profile , url , proxyUrl , userAgent ) {
722723 try {
723724 if ( Math . random ( ) < profile . idleBias ) {
724725 console . log ( chalk . hex ( "#808080" ) . italic ( "\n😴 Persona idle mode, skipping this cycle..." ) ) ;
@@ -753,7 +754,7 @@ async function sendTx(wallet, provider, profile, url) {
753754
754755 // === NEW: Dynamic Gas Pricing Check ===
755756 // Check gas prices before transaction (less verbose)
756- const gasData = await getAverageGasPrice ( true ) ; // Verbose for initial check
757+ const gasData = await getAverageGasPrice ( true , proxyUrl , userAgent ) ; // Verbose for initial check
757758
758759 if ( ! gasData ) {
759760 console . error ( chalk . red ( "❌ Failed to get gas price data, skipping transaction" ) ) ;
@@ -769,14 +770,14 @@ async function sendTx(wallet, provider, profile, url) {
769770 }
770771
771772 // Check for gas price spikes and implement human-like hesitation
772- const shouldProceed = await shouldProceedWithGasSpike ( avgGasPriceGwei , gasPricesGwei ) ;
773+ const shouldProceed = await shouldProceedWithGasSpike ( avgGasPriceGwei , gasPricesGwei , proxyUrl , userAgent ) ;
773774 if ( ! shouldProceed ) {
774775 console . log ( chalk . yellow ( "⏳ Skipping transaction due to gas price conditions" ) ) ;
775776 return ;
776777 }
777778
778779 // Get updated gas data after any hesitation (less verbose)
779- const finalGasData = await getAverageGasPrice ( false ) ;
780+ const finalGasData = await getAverageGasPrice ( false , proxyUrl , userAgent ) ;
780781 if ( ! finalGasData || ! isGasPriceAcceptable ( finalGasData . avgGasPriceGwei ) ) {
781782 console . log ( chalk . yellow ( `⛽ Gas price ${ finalGasData ?. avgGasPriceGwei ?. toFixed ( 2 ) || 'N/A' } Gwei above threshold after hesitation, skipping transaction` ) ) ;
782783 return ;
@@ -891,10 +892,12 @@ async function sendTx(wallet, provider, profile, url) {
891892 * @param {ethers.JsonRpcProvider } provider - The RPC provider.
892893 * @param {object } profile - The wallet's persona profile.
893894 * @param {string } url - The URL of the RPC being used.
895+ * @param {string } proxyUrl - The proxy URL being used.
896+ * @param {string } userAgent - The user agent being used.
894897 */
895- async function safeSendTx ( wallet , provider , profile , url ) {
898+ async function safeSendTx ( wallet , provider , profile , url , proxyUrl , userAgent ) {
896899 try {
897- await sendTx ( wallet , provider , profile , url ) ;
900+ await sendTx ( wallet , provider , profile , url , proxyUrl , userAgent ) ;
898901 } catch ( err ) {
899902 console . warn ( chalk . hex ( "#FFA500" ) . bold ( "⚠️ Transaction failed. Checking persona retry bias..." ) ) ;
900903
@@ -909,7 +912,7 @@ async function safeSendTx(wallet, provider, profile, url) {
909912
910913 console . warn ( chalk . hex ( "#FFA500" ) . bold ( "⚠️ Retrying after error..." ) ) ;
911914 await randomDelay ( 5 , 10 ) ;
912- try { await sendTx ( wallet , provider , profile , url ) ; } catch ( retryErr ) {
915+ try { await sendTx ( wallet , provider , profile , url , proxyUrl , userAgent ) ; } catch ( retryErr ) {
913916 console . error ( chalk . bgRed . white . bold ( "❌ Error on retry:" , retryErr . message ) ) ;
914917 }
915918 }
@@ -999,11 +1002,15 @@ async function main() {
9991002 // === NEW LOGIC: Retry loop for RPC connection ===
10001003 let provider = null ;
10011004 let url = null ;
1005+ let proxyUrl = null ;
1006+ let userAgent = null ;
10021007 while ( ! provider ) {
10031008 const providerResult = await getWorkingProvider ( profile ) ;
10041009 if ( providerResult ) {
10051010 provider = providerResult . provider ;
10061011 url = providerResult . url ;
1012+ proxyUrl = providerResult . proxyUrl ;
1013+ userAgent = providerResult . userAgent ;
10071014 } else {
10081015 console . warn ( chalk . hex ( "#FF8C00" ) . bold ( "🚫 All RPCs failed to connect. Retrying in 10 seconds..." ) ) ;
10091016 await randomDelay ( 10 , 15 ) ;
@@ -1021,7 +1028,7 @@ async function main() {
10211028 }
10221029
10231030 // Execute the transaction logic, including retries
1024- await safeSendTx ( wallet , provider , profile , url ) ;
1031+ await safeSendTx ( wallet , provider , profile , url , proxyUrl , userAgent ) ;
10251032
10261033 // Explicit memory wipe after use
10271034 key = null ;
0 commit comments