Skip to content

Commit 5ac68f1

Browse files
authored
Update index.js
1 parent 37f14f4 commit 5ac68f1

File tree

1 file changed

+45
-38
lines changed

1 file changed

+45
-38
lines changed

index.js

Lines changed: 45 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -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 ===
7373
const ALGO = "aes-256-gcm";
7474
const IV_LENGTH = 12;
7575

@@ -87,40 +87,34 @@ if (!MASTER_KEY) {
8787

8888
const 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
*/
114106
function 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
*/
132126
function 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
438434
function 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
*/
654653
async 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
*/
680679
async 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

Comments
 (0)