26 Minuten Lesezeit
Bei der Verwendung Solana gRPC Node.js für sehr große Datenmengen kann es vorkommen, dass eine einzelne CPU überlastet wird. Wenn Sie Systeme entwickeln, die alle Transaktionen aus stark ausgelasteten Programmen verarbeiten oder mehrere Programme gleichzeitig überwachen müssen, sollten Sie die folgenden leistungsstarken Alternativen in Betracht ziehen:
-
Verwendung mehrerer Threads. Node.js verfügt über zwei Module:
Cluster(Mehrprozessbetrieb, ein Thread pro Prozess) undArbeitnehmer(ein Prozess, mehrere Threads). Beides löst das Problem, dass ein Knoten einen einzelnen Kern überlastet, aberClusterist einfacher, da jeder Prozess speicherisoliert ist. Verteilen Sie die Solana gRPC auf mehrere Instanzen vonClusteroderArbeitnehmerum die Last zu verteilen. -
Verwendung von Solana gRPC Rust (siehe auch Anleitung zuSolana gRPC Rust)
-
Verwendung von Solana gRPC Go (siehe auch die Anleitung zuSolana gRPC Go).
Übersicht
In dieser Anleitung erfahren wir, wie man einen Solana erstellt, der die Trades einer bestimmten Wallet auf der Pump.fun-DEX mithilfe des Metis-Add-onsQuicknode für die Pump.fun-API kopiert, und Solana gRPC (Yellowstone Geyser gRPC) kopiert. Diese Anleitung richtet sich an Entwickler mit fundierten Kenntnissen in JavaScript, Solana und grundlegenden DeFi-Konzepten.
Lernst du am liebsten visuell? Dann schau dir dieses Video an und erfahre, wie du mit Pump.fun einen Solana erstellst.
Ihre Aufgaben
- Verschaffen Sie sich einen Überblick über Pump.fun und Solana gRPC
- Erstelle einen JavaScript-Trading-Bot, der die Transaktionen von Wallets auf Pump.fun überwacht und deren Kauftransaktionen anhand einer vordefinierten Strategie nachahmt.
- Simuliere Transaktionen im Ziel-Wallet und teste die Funktionalität des Bots
Wenn Sie lieber mit TypeScript oder der neuen Solana arbeiten, lesen Sie unseren Leitfaden zum Thema Solana mit Solana gRPC TypeScript) überwachen“ und erfahren Sie, wie Sie mit der Pump.fun-API und Solana entwickeln können.
Was Sie benötigen
- Fortgeschrittene Kenntnisse in Solana und in DeFi-Konzepten
- Erfahrung mit JavaScript und Node.js
- Eine Solana -Wallet mit einem SOL-Guthaben (ausführen
solana-h(für Unterstützung beim Einrichten einer neuen Wallet) - Ein Quicknode mitgRPC (im Rahmen der Tarife „Scale“ und „Business“ enthalten oder über das gRPC bei den Tarifen „Build“ und „Accelerate“ erhältlich)
Stellen Sie über Ihren Quicknode Endpoint eine Verbindung zu einem Solana her
Um auf Solana zu entwickeln, benötigen Sie einen endpoint eine Verbindung zum Netzwerk herzustellen. Sie können gerne öffentliche Knoten nutzen oder Ihre eigene Infrastruktur bereitstellen und verwalten; wenn Sie jedoch achtmal schnellere Antwortzeiten wünschen, können Sie die mühsame Arbeit gerne uns überlassen.
Erfahren Sie, warum über 50 % der Projekte auf Solana Entscheiden Sie sich für Quicknode starten Sie Ihre kostenlose Testphase hier. Wir werden ein Solana Mainnet endpoint.
Kopiere die HTTP Link zum Anbieter:
Was ist Metis?
Metis is a powerful tool for developers to access liquidity on Solana. By integrating Jupiter's V6 Swap API, Jupiter's Limit Order API, Pump.fun trading, Trading websockets, and more, you have access to the tools you need to build robust trading tools that access many DeFi protocols across Solana. Metis is available as an add-on on the Quicknode Add-ons, here or a public endpoint is available at JupiterAPI.com.
Verwendung der Pump.fun-API
In diesem Leitfaden konzentrieren wir uns in erster Linie auf die /pump-fun/swap endpointDokumente), über den wir eine serialisierte Transaktion abrufen können, um einen Swap auf Pump.fun durchzuführen. Dieser endpoint die folgenden Parameter:
Geldbörse: Der öffentliche Schlüssel der Wallet, die den Handel ausführtTyp: Die Art des Handels („KAUF“ oder „VERKAUF“)Minze: Die Mint-Adresse des gehandelten TokensinAmount: Die Menge des Eingabetokens (in Roh-Einheiten)priorityFeeLevel(optional): Die Prioritätsstufe für den Handel („niedrig“, „mittel“, „hoch“ oder „auto“)Slippage in Basispunkten(optional): Die für das Geschäft zulässige maximale Slippage (in Basispunkten)
Die Antwort dieses endpoint eine Base64-kodierte Solana , die signiert und an das Netzwerk gesendet werden kann, um den Handel auszuführen.
Was ist Solana gRPC?
Solana gRPC ist gRPC Yellowstone gRPC Quicknode. Er bietet eine gRPC API, mit der Entwickler benutzerdefinierte Abonnements erstellen und Echtzeit-Updates erhalten können, sobald Ereignisse im Solana eintreten. Damit eignet er sich hervorragend für die Entwicklung von Anwendungen, die eine Echtzeitüberwachung der Blockchain-Aktivitäten erfordern, wie beispielsweise Trading-Bots, Analyseplattformen und dezentrale Anwendungen (dApps).
Um Solana gRPC zu nutzen, müssen wir eine Abonnementanfrage erstellen, in der die Konten, Transaktionen und sonstigen Ereignisse angegeben sind, die wir überwachen möchten. Solana gRPC dann Echtzeit-Updates zu den angegebenen Ereignissen.
Solana gRPC in den Tarifen „Scale“ und „Business“ enthalten. In den Tarifen „Build“ und „Accelerate“ ist es weiterhin über das Solana gRPC verfügbar.
Weitere Informationen zu Solana gRPC finden Sie unter:
Einrichten des Projekts
Dieser Leitfaden dient ausschließlich zu Informationszwecken und ist nicht als Finanzberatung zu verstehen. Der Einsatz von Trading-Bots kann mit Risiken verbunden sein und zu finanziellen Verlusten führen. Führen Sie stets eigene Recherchen durch und ziehen Sie gegebenenfalls einen Finanzberater zu Rate, bevor Sie Trading-Bots einsetzen oder Handelsaktivitäten durchführen. Vergewissern Sie sich, dass der von Ihnen verwendete Code sicher und gründlich getestet ist, bevor Sie ihn mit echtem Geld einsetzen.
Bevor wir mit der Entwicklung des Trading-Bots beginnen, richten wir zunächst unser Projekt ein und installieren die erforderlichen Abhängigkeiten.
-
Erstellen Sie ein neues Verzeichnis für Ihr Projekt und wechseln Sie in Ihrem Terminal in dieses Verzeichnis.
-
Initialisieren Sie ein neues Node.js-Projekt, indem Sie den folgenden Befehl ausführen:
npm init -y -
Installieren Sie die erforderlichen Abhängigkeiten, indem Sie den folgenden Befehl ausführen:
npm install @solana/web3.js@1 bs58 dotenv @triton-one/yellowstone-grpcsolana.js: Die ältere Version der Solana .js-Bibliothek für die Interaktion mit der Solanabs58: Eine Bibliothek für die Arbeit mit der Base58-Kodierung und -Dekodierungdotenv: Eine Bibliothek zum Laden von Umgebungsvariablen aus einer.envDateigrpc: Die Yellowstone gRPC -Bibliothek
-
Erstellen Sie eine neue Datei mit dem Namen
bot.jsin Ihrem Projektverzeichnis. -
Erstellen Sie ein
.envDatei in Ihrem Projektverzeichnis und fügen Sie die folgenden Umgebungsvariablen hinzu:SOLANA_RPC=<your_solana_rpc_endpoint> # https://example.quiknode.pro/replace-me-123/SECRET_KEY=<your_wallet_secret_key> # [0, 0, ..., 0]METIS_ENDPOINT=<your_metis_endpoint> # https://jupiter-swap-api.quiknode.pro/REPLACE_MEYELLOWSTONE_ENDPOINT=<your_yellowstone_endpoint> # https://example.solana-mainnet.quiknode.pro:10000YELLOWSTONE_TOKEN=<your_yellowstone_token> # abc...xyzErsetzen Sie die Platzhalter durch Ihre tatsächlichen Werte:
SOLANA: Ihr Quicknode Mainnet endpoint fürMainnet Solana Mainnet endpoint diesen finden Sie in Ihrer Quicknode)SECRET_KEY: Der geheime Schlüssel Ihrer Solana (im JSON-Array-Format, z. B.[0, 0, ..., 0]). Stellen Sie sicher, dass diese Wallet mit SOL aufgeladen ist, damit der Bot Trades ausführen kann.METIS_ENDPOINT: Ihr Quicknode endpoint die Pump.fun-API (z. B.https://jupiter-swap-api.quiknode.pro/...). Falls Sie das Metis-Add-on nicht haben, können Sie den öffentlichen endpoint nutzen:https://public.jupiterapi.com(Hinweis: Für den öffentlichen endpoint Transaktionsgebühren anfallen – bitte prüfen Sie dies jupiterapi.com (weitere Informationen).YELLOWSTONE: Ihr Solana gRPC endpoint Hinweis: Es sollte sich um einen gRPC endpoint handeln, der auf:10000- Weitere Informationen finden Sie hier hier)YELLOWSTONE: Ihr Solana gRPC -Token (suchen Sie Ihr Token hier)
Entwicklung des Trading-Bots
Nachdem wir unser Projekt nun eingerichtet haben, fangen wir mit der Entwicklung des Trading-Bots an. 🤖
Im Großen und Ganzen werden wir Folgendes tun:
- Erstellen Sie eine Klasse für den Trading-Bot, die die erforderlichen Konfigurationen und Abhängigkeiten initialisiert.
- Erstellen Sie eine Überwachungsfunktion, die mithilfe von Solana gRPC auf Kauftransaktionen für die Ziel-Wallet auf Pump.fun überwacht.
- Implementieren Sie eine Methode, die auf Zieltransaktionen reagiert, indem sie mithilfe der Pump.fun-API eine Kopie der Swap-Transaktion abruft und den Handel auf Solana ausführt.
- Erfolge bei Transaktionen zur Nachverfolgung und Analyse in einer Datei protokollieren.
Den Bot konfigurieren
Öffnen Sie die bot.js Datei und fügen Sie den folgenden Code hinzu:
require("dotenv").config();
const fs = require("fs");
const fetch = require("node-fetch");
const bs58 = require("bs58").default;
const {
Connection,
Keypair,
VersionedTransaction,
LAMPORTS_PER_SOL,
PublicKey,
} = require("@solana/web3.js");
const Client = require("@triton-one/yellowstone-grpc").default;
const { CommitmentLevel } = require("@triton-one/yellowstone-grpc");
class CopyTradeBot {
config = {
WATCH_LIST: [
"WALLET_TO_TRACK_1",
"WALLET_TO_TRACK_2",
"WALLET_TO_TRACK_3",
//...
],
PUMP_FUN: {
PROGRAM_ID: "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P",
FEE_ACCOUNT: "CebN5WGQ4jvEPvsVU4EoHEpgzq1VV7AbicfhtW4xC9iM",
BUY_DISCRIMINATOR: Buffer.from([102, 6, 61, 18, 1, 218, 235, 234]),
SELL_DISCRIMINATOR: Buffer.from([51, 230, 133, 164, 1, 127, 131, 173]),
TOKEN_DECIMALS: 6,
TARGET_ACCOUNTS: {
BUY: [
{ name: "mint", index: 2 },
{ name: "user", index: 6 },
],
SELL: [
{ name: "mint", index: 2 },
{ name: "user", index: 6 },
],
},
},
MIN_TX_AMOUNT: LAMPORTS_PER_SOL / 1000,
BUY_AMOUNT: LAMPORTS_PER_SOL / 1000,
LOG_FILE: "pump_fun_swaps.json",
COMMITMENT: CommitmentLevel.CONFIRMED,
TEST_MODE: true
};
constructor() {
this.validateEnv();
this.connection = new Connection(process.env.SOLANA_RPC);
this.wallet = Keypair.fromSecretKey(
Uint8Array.from(JSON.parse(process.env.SECRET_KEY))
);
console.log("🤖 Bot wallet:", this.wallet.publicKey.toBase58());
console.log("Watching addresses:");
this.config.WATCH_LIST.forEach((address) => console.log(" -", address));
}
// ... (other methods will be added here)
}
Dieser Code legt die Anfangskonfiguration für unseren Trading-Bot fest. Die einzelnen Teile der Konfiguration haben folgende Funktionen:
BEOBACHTUNGSLISTE: Ein Array von Wallet-Adressen, die der Bot auf Pump.fun auf Transaktionen überwacht. In diesem Beispiel, dann verwenden Sie einfach eine Wallet-Adresse aus Ihrer eigenen Browser-Wallet (z. B. Phantom, SolFlare, Backpack usw.). Diese Wallet werden wir später in dieser Anleitung verwenden, um einen Handel auf Pump.fun durchzuführen.PUMP_FUN: Ein Objekt, das Konfigurationsdetails für das Programm „Pump.fun“ enthält, darunter die Programm-ID, das Gebührenkonto, Unterscheidungsmerkmale für Kauf- und Verkaufsaufträge, die Dezimalstellen der Token sowie Indizes für die Zielkonten (Mint und Benutzer) in den Aufträgen. Dabei handelt es sich um bekannte Konstanten, die abgeleitet sind von Das Programm IDL und die Anleitung von Pump.fun. Weitere Informationen zu Programm-IDLs finden Sie in unserem Leitfaden zur Solana IDL.MIN_TX_AMOUNT: Der Mindestbetrag in SOL, der erforderlich ist, damit der Bot eine Transaktion in Betracht zieht (wenn die Ziel-Wallet weniger als diesen Betrag ausgibt, ignorieren wir die Transaktion einfach).KAUF_MENGE: Die Menge an SOL, die der Bot zur Ausführung von Kauftransaktionen verwenden wird. Für diese Demo legen wir einfach einen festen Kaufbetrag fest – wenn wir eine Transaktion sehen, die wir kopieren möchten, geben wir einen festen Betrag an SOL für einen Kopier-Trade aus. Nachdem Sie die Anleitung durchgearbeitet haben, können Sie gerne mit verschiedenen Strategien für Ihre eigenen Anwendungsfälle experimentieren.LOG_FILE: Der Dateipfad, in dem der Bot erfolgreiche Trades protokolliert.ENGAGEMENT: Der Verpflichtungsgrad für Solana gRPC .TEST_MODUS: Ein Flag zum Aktivieren/Deaktivieren mode. Wenn aufwahr, simuliert der Bot Trades, ohne diese tatsächlich im Solana auszuführen. Dies ist nützlich, um die Funktionalität des Bots zu testen, ohne echtes Geld zu riskieren.
Die Konstruktor Die Methode überprüft die erforderlichen Umgebungsvariablen, stellt eine Solana her und initialisiert die Wallet des Bots mithilfe des angegebenen geheimen Schlüssels.
Fügen wir unsere Methoden hinzu!
Überprüfung der Umgebungsvariablen
Wir haben bereits ein validateEnv() Methode im obigen Konstruktor, die sicherstellt, dass die erforderlichen Umgebungsvariablen gesetzt sind. Diese Methode wird unmittelbar dann ausgelöst, wenn die CopyTradeBot Die Klasse wird instanziiert. Falls erforderliche Variablen fehlen, löst der Bot einen Fehler aus und wird beendet.
Fügen Sie Ihrer CopyTradeBot -Klasse die folgende Methode hinzu, um die erforderlichen Umgebungsvariablen zu überprüfen:
validateEnv = () => {
const requiredEnvs = [
"SOLANA_RPC",
"SECRET_KEY",
"METIS_ENDPOINT",
"YELLOWSTONE_ENDPOINT",
"YELLOWSTONE_TOKEN",
];
requiredEnvs.forEach((env) => {
if (!process.env[env]) {
throw new Error(`Missing required environment variable: ${env}`);
}
});
};
Hiermit wird lediglich überprüft, ob alle erforderlichen Umgebungsvariablen gesetzt sind, bevor fortgefahren wird.
Abrufen der Swap-Transaktion
Lassen Sie uns ein fetchSwapTransaction() Methode, die für die Kommunikation mit der Pump.fun-API (über Ihren endpoint) zuständig ist, um eine serialisierte Transaktion abzurufen, die einen Swap durchführt. Sie geben dabei Details wie die Wallet-Adresse, die Art des Swaps (z. B. „BUY“), den zu prägenden Token und den zu tauschenden Betrag an.
fetchSwapTransaction = async ({
wallet,
type,
mint,
inAmount,
priorityFeeLevel = "high",
slippageBps = "100",
}) => {
const body = JSON.stringify({
wallet,
type,
mint,
inAmount,
priorityFeeLevel,
slippageBps,
});
const res = await fetch(`${process.env.METIS_ENDPOINT}/pump-fun/swap`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body,
});
if (!res.ok) {
throw new Error(`Swap instructions fetch error: ${await res.text()}`);
}
return res.json();
};
Hier sind die beiden Hauptkomponenten dieser Methode:
Körper: Enthält die Parameter, die die Pump.fun-API benötigt, um eine Swap-Transaktion zu generieren.holen: Sendet eine POST-Anfrage endpoint den angegebenen Parametern an den endpoint „/swap“ von Pump.fun. Bei erfolgreichem Abschluss erhalten Sie eine JSON-Antwort, die eine Base64-kodierte Transaktion enthält.
Weitere Informationen zu dieser Methode findest du hier in unserer Pump.fun-API-Dokumentation.
Unterzeichnung der Transaktion
Sobald die Pump.fun-API eine Base64-kodierte Transaktion zurückgibt, musst du diese lokal mit dem Schlüsselpaar deines Bots signieren. Fügen wir einen signTransaction() Methode, die dies übernimmt, in unsere CopyTradeBot Klasse:
signTransaction = async (swapTransaction) => {
const transaction = VersionedTransaction.deserialize(
Buffer.from(swapTransaction, "base64")
);
const latestBlockHash = await this.connection.getLatestBlockhash();
transaction.message.recentBlockhash = latestBlockHash.blockhash;
transaction.sign([this.wallet]);
const txBuffer = Buffer.from(transaction.serialize());
const txBase64 = txBuffer.toString("base64");
return txBase64;
};
Da die Transaktion bereits serialisiert ist, müssen wir sie in ein Versionsgesteuerte Transaktion Objekt, signieren Sie es mit der Wallet unseres Bots und serialisieren Sie es anschließend wieder in Rohdaten, um es zu übertragen:
- Deserialisierung: Wandelt die Base64-kodierte Transaktion in eine
Versionsgesteuerte TransaktionObjekt. - Neuesten Blockhash abrufen: Ruft den neuesten Blockhash aus dem Solana ab und wendet ihn auf die Transaktion an.
- Signieren: Fügt der Transaktion die Wallet-Signatur Ihres Bots hinzu.
- Serialize: Gibt die vollständig signierte Transaktion als Base64-kodierte Zeichenkette zurück, die zur Übertragung bereit ist.
Transaktion senden und bestätigen
Der nächste Schritt besteht darin, die signierte Transaktion an das Solana zu senden. Fügen Sie Folgendes hinzu: sendAndConfirmTransaction() Methode zu Ihrer Bot-Klasse:
sendAndConfirmTransaction = async (signedTxBase64) => {
try {
const txid = await this.connection.sendEncodedTransaction(signedTxBase64, {
skipPreflight: false,
encoding: 'base64'
});
const timeout = 30 * 1000;
const pollInterval = 3 * 1000;
const start = Date.now();
while (Date.now() - start < timeout) {
const response = await this.connection.getSignatureStatuses([txid]);
if (!response) {
await new Promise(resolve => setTimeout(resolve, pollInterval));
continue;
}
const statuses = response.value;
if (!statuses || statuses.length === 0) {
await new Promise(resolve => setTimeout(resolve, pollInterval));
continue;
}
const status = statuses[0];
if (status === null) {
await new Promise(resolve => setTimeout(resolve, pollInterval));
continue;
}
if (status.err) {
throw new Error(`Transaction failed: ${JSON.stringify(status.err)}`);
}
if (status.confirmationStatus && (status.confirmationStatus === 'confirmed' || status.confirmationStatus === 'finalized')) {
return txid;
}
await new Promise(resolve => setTimeout(resolve, pollInterval));
}
throw new Error(`Transaction confirmation timeout after ${timeout}ms`);
} catch (error) {
throw {error, base64: Buffer.from(rawTransaction).toString("base64")};
}
};
Bei dieser Methode wird die signierte Transaktion an das Solana gesendet und auf deren Bestätigung gewartet. Dabei werden die folgenden Schritte durchgeführt:
sendEncodedTransaction(): Sendet die als Base64-Zeichenkette kodierte signierte Transaktion an den Cluster und gibt eine Transaktionssignatur (txid) zurück – beachten Sie, dass wir, da wir die Transaktion im vorherigen Schritt serialisiert haben, diesendEncodedTransaction()Methode. Die API, die die Transaktion erstellt, simuliert diese bereits zur Berechnung der Recheneinheiten, sodass wir die Vorabprüfungen überspringen können.- Zum Schluss erstellen wir eine einfache Abfragefunktion mit
getSignatureStatuses()um den Status der Transaktion zu überprüfen, bis sie bestätigt wird oder die Zeitüberschreitung eintritt. Weitere Informationen zu bewährten Vorgehensweisen finden Sie in unserer Dokumentation hier.
Swaps protokollieren
Zur Protokollierung und Fehlerbehebung wird die logSwap() Die Methode schreibt für jeden Swap einen JSON-Eintrag in eine Protokolldatei:
logSwap = (swapLog) => {
const logs = fs.existsSync(this.config.LOG_FILE)
? JSON.parse(fs.readFileSync(this.config.LOG_FILE, "utf-8"))
: [];
logs.push(swapLog);
fs.writeFileSync(this.config.LOG_FILE, JSON.stringify(logs, null, 2));
};
Und so gehen wir vor:
- Vorhandene Protokolle: Wenn die Datei
pump_fun_swaps.jsonWenn diese vorhanden ist, wird sie gelesen und in ein Array geparst. - Neues Protokoll hinzufügen: Das
swapLogDas Objekt wird dem Array hinzugefügt. - Zurückschreiben: Schreibt das aktualisierte Array auf die Festplatte.
Umgang mit Großaufträgen
Sobald wir eine Kauftransaktion oberhalb eines bestimmten Schwellenwerts feststellen (MIN_TX_AMOUNT) von einem „Großkunden“ in unserem BEOBACHTUNGSLISTE, müssen wir eine bestimmte Logik ausführen. Erstellen wir eine Methode, handleWhaleBuy(), die einen Copy-Trade ausführt, sobald wir wissen, dass ein „Whale“ einen Kauf getätigt hat, der unseren Kriterien entspricht. Füge die folgende Methode zu deiner CopyTradeBot Klasse:
handleWhaleBuy = async (whalePubkey, tokenMint, lamportsSpent, copiedTxid) => {
if (lamportsSpent < this.config.MIN_TX_AMOUNT) return;
try {
const inAmount = this.config.BUY_AMOUNT;
const response = await this.fetchSwapTransaction({
wallet: this.wallet.publicKey.toBase58(),
type: "BUY",
mint: tokenMint,
inAmount,
slippageBps: "300",
});
if (!response.tx) {
throw new Error(`Unexpected response format: ${JSON.stringify(response)}`);
}
const { tx } = response;
const signedTransaction = await this.signTransaction(tx);
let txid = 'Simulated-TxID';
if (!this.config.TEST_MODE) {
txid = await this.sendAndConfirmTransaction(signedTransaction);
}
console.log("🎯 - COPY - TxID:", txid);
this.logSwap({
event: "COPY_BUY",
txid,
copiedTxid,
tokenMint,
lamportsSpent,
whalePubkey,
timestamp: new Date().toISOString(),
});
} catch (err) {
this.logSwap({
event: "COPY_BUY_ERROR",
error:
typeof err === "string"
? err
: err && typeof err.message === "string"
? err.message
: JSON.stringify(err, null, 2) || "Unknown error",
copiedTxid,
timestamp: new Date().toISOString(),
});
}
};
Werfen wir einen Blick auf die wichtigsten Bestandteile dieser Methode:
Ausgegebene Lamports: Die Gesamtmenge an SOL, die die Ziel-Wallet in der ursprünglichen Transaktion ausgegeben hat. Diese muss den Wert MIN_TX_AMOUNT überschreiten, damit wir sie überhaupt kopieren. Sie können hier natürlich Ihre eigene Logik implementieren.inAmount: Wie viel SOL unser Bot für das Kopieren des Trades ausgeben wird. Der Wert ist aufKAUF_MENGEaus unserer Konfiguration.fetchSwapTransaction(): Wir beantragen eine Pump.fun-Transaktion für unseren eigenen Kauf.signTransaction(): Unterschreibe es mit unserem Portemonnaie.sendAndConfirmTransaction(): Die signierte Transaktion an das Solana senden und auf die Bestätigung warten. (wird übersprungen, wennTEST_MODUS(ist aktiviert)logSwap(): Den Versuch eines Copy-Trades in unserer Protokolldatei festhalten.
Einrichten des Solana gRPC
Nachdem wir nun unsere Methoden zur Ausführung des Bots eingerichtet haben, müssen wir ein Abonnement für Solana gRPC einrichten,gRPC die Ziel-Wallets auf Kauftransaktionen auf Pump.fungRPC überwachen.gRPC Solana gRPC wir nach Transaktionen Ausschau halten, die unseren spezifischen Kriterien entsprechen. Anschließend können wir die Transaktionsdaten auswerten, um sicherzustellen, dass wir angemessen reagieren.
Abonnementanfrage erstellen
Wir müssen Solana gRPC mitteilen,gRPC Adressen oder Programme wir überwachen möchten. Fügen wir unserer CopyTradeBot -Klasse ein paar Methoden hinzu:
createSubscribeRequest = () => {
const { WATCH_LIST, PUMP_FUN, COMMITMENT } = this.config;
return {
accounts: {},
slots: {},
transactions: {
pumpFun: {
accountInclude: WATCH_LIST,
accountExclude: [],
accountRequired: [PUMP_FUN.FEE_ACCOUNT, PUMP_FUN.PROGRAM_ID],
},
},
transactionsStatus: {},
entry: {},
blocks: {},
blocksMeta: {},
commitment: COMMITMENT,
accountsDataSlice: [],
ping: undefined,
};
};
sendSubscribeRequest = (stream, request) => {
return new Promise((resolve, reject) => {
stream.write(request, (err) => {
if (err) reject(err);
else resolve();
});
});
};
handleStreamEvents = (stream) => {
return new Promise((resolve, reject) => {
stream.on("data", this.handleData);
stream.on("error", (error) => {
console.error("Stream error:", error);
reject(error);
stream.end();
});
stream.on("end", () => {
console.log("Stream ended");
resolve();
});
stream.on("close", () => {
console.log("Stream closed");
resolve();
});
});
};
Hier definieren wir drei Funktionen:
createSubscribeRequest(): Erstellt das Abonnementanfrage-Objekt mit den für uns relevanten Konten und Filtern.Erforderliche Kontenstellt sicher, dass wir nur Transaktionen sehen, an denen das Gebührenkonto und die Programm-ID von Pump.fun beteiligt sind – anhand des Gebührenkontos können wir Pump.fun-Transaktionen von anderen Transaktionsarten im Programm unterscheiden. DieaccountIncludeDer Filter stellt sicher, dass wir nur Transaktionen aus unseremBEOBACHTUNGSLISTEWallets (stellen Sie sich dies als eine „ODER“-Verknüpfung vor – solange eine Transaktion also eines dieser Konten enthält, wird sie angezeigt).sendSubscribeRequest(): Sendet das Abonnement an das Netzwerk.handleStreamEvents(): Beschreibt, wie wir mit eingehenden Daten, Fehlern und dem Schließen des Streams umgehen. Wichtig ist, dass wir hierthis.handleDatawenn wir neue Daten erhalten. Mit anderen Worten: wenn Solana gRPC eine TransaktiongRPC , die unseren Filterkriterien entspricht (definiert increateSubscribeRequest()), werden wir diese mithilfe derhandleData()Methode.
Verarbeitung eingehender Transaktionen
Die handleData() In dieser Methode analysieren wir die Transaktionsdetails von „Pump.fun“. Dies ist für unseren Anwendungsfall äußerst wichtig, da wir hier ermitteln, um welche Art von Transaktion es sich handelt (z. B. Kauf oder Verkauf) und wie groß das Handelsvolumen ist. Fügen Sie die folgende Methode zu Ihrer Bot-Klasse hinzu; im Folgenden werden wir sie im Detail durchgehen:
handleData = (data) => {
if (
!this.isSubscribeUpdateTransaction(data) ||
!data.filters.includes("pumpFun")
) {
return;
}
const transaction = data.transaction?.transaction;
const message = transaction?.transaction?.message;
const innerInstructions = transaction?.meta?.innerInstructions;
const flattenedInnerInstructions =
innerInstructions?.flatMap((ix) => ix.instructions || []) || [];
const allInstructions = [
...message.instructions,
...flattenedInnerInstructions,
];
if (!transaction || !message || transaction?.meta?.err) return;
const formattedSignature = this.convertSignature(transaction.signature);
const matching = allInstructions.find(this.matchesInstructionDiscriminator);
if (!matching) {
console.log(`❓ - Unknown - TxID: ${formattedSignature.base58}`);
return;
}
const { amount, solAmount } = this.getInstructionData(matching.data);
if (solAmount < this.config.MIN_TX_AMOUNT) return;
const txType = this.getTransactionType(matching.data);
const icon = txType === "SELL" ? "📉" : txType === "BUY" ? "🎯" : "❓";
console.log(`${icon} - ${txType} - TxID: ${formattedSignature.base58}`);
const accountKeys = message.accountKeys;
const accountsToInclude = this.config.PUMP_FUN.TARGET_ACCOUNTS[txType];
const includedAccounts = accountsToInclude.reduce((acc, { name, index }) => {
const accountIndex = matching.accounts[index];
const publicKey = accountKeys[accountIndex];
acc[name] = new PublicKey(publicKey).toBase58();
return acc;
}, {});
if (includedAccounts.mint) {
console.log(" Mint:", includedAccounts.mint);
}
if (includedAccounts.user) {
console.log(" User:", includedAccounts.user);
}
console.log(
" Token Amount:",
amount / Math.pow(10, this.config.PUMP_FUN.TOKEN_DECIMALS)
);
console.log(" SOL Amount:", solAmount / LAMPORTS_PER_SOL);
if (txType === "BUY") {
(async () => {
try {
await this.handleWhaleBuy(
includedAccounts.user,
includedAccounts.mint,
solAmount,
formattedSignature.base58
);
} catch (error) {
console.error("Error in handleWhaleBuy:", error);
}
})();
}
};
Das machen wir
- Zunächst stellen wir sicher, dass es sich bei den eingehenden Daten um eine Transaktionsaktualisierung von Solana gRPC handeltgRPC dass das Programm „Pump.fun“ daran beteiligt ist. Sollte dies aus irgendeinem Grund nicht der Fall sein, ignorieren wir die Daten.
- Anschließend extrahieren wir die Transaktionsdaten und Anweisungen aus den eingehenden Daten. Wir fassen alle Anweisungen in einem einzigen Array zusammen, um die Verarbeitung der inneren und äußeren Anweisungen zu vereinfachen.
- Wir prüfen, ob es sich bei der Transaktion auf Pump.fun um einen Kauf oder Verkauf handelt – diese Methode werden wir in Kürze definieren.
- Anschließend extrahieren wir den Betrag und die verbrauchten SOL aus den Transaktionsdaten.
- Wenn die Transaktion einen „Pump.fun BUY“ enthält: Wir rufen
handleWhaleBuy()mit den entsprechenden Transaktionsdaten.
Dir wird auffallen, dass wir hier einige Hilfsmethoden verwendet haben, die wir noch nicht definiert haben. Fügen wir diese nun hinzu. Füge diese verbleibenden Hilfsmethoden zu deiner Bot-Klasse hinzu.
isSubscribeUpdateTransaction = (data) => {
return (
"transaction" in data &&
typeof data.transaction === "object" &&
data.transaction !== null &&
"slot" in data.transaction &&
"transaction" in data.transaction
);
};
convertSignature = (signature) => {
return { base58: bs58.encode(Buffer.from(signature)) };
};
parseU64 = (data, offset) => {
const slice = data.slice(offset, offset + 8);
const dataView = new DataView(
slice.buffer,
slice.byteOffset,
slice.byteLength
);
return Number(dataView.getBigUint64(0, true));
};
getInstructionData = (instructionData) => {
const amount = this.parseU64(instructionData, 8);
const solAmount = this.parseU64(instructionData, 16);
return { amount, solAmount };
};
getTransactionType = (instructionData) => {
if (!instructionData) return "Unknown";
if (
this.config.PUMP_FUN.SELL_DISCRIMINATOR.equals(
instructionData.slice(0, 8)
)
) {
return "SELL";
} else if (
this.config.PUMP_FUN.BUY_DISCRIMINATOR.equals(
instructionData.slice(0, 8)
)
) {
return "BUY";
}
return "Unknown";
};
matchesInstructionDiscriminator = (ix) => {
if (!ix?.data) return false;
return (
this.config.PUMP_FUN.SELL_DISCRIMINATOR.equals(ix.data.slice(0, 8)) ||
this.config.PUMP_FUN.BUY_DISCRIMINATOR.equals(ix.data.slice(0, 8))
);
};
Lassen Sie uns jeden dieser Punkte näher erläutern:
isSubscribeUpdateTransaction(): Prüft, ob es sich bei den eingehenden Daten um ein gültiges Transaktionsobjekt von Solana gRPC handelt.convertSignature(): Wandelt die Transaktionssignatur zur einfacheren Protokollierung in eine Base58-kodierte Zeichenfolge um.parseU64(): Analysiert eine 64-Bit-Ganzzahl ohne Vorzeichen aus den Befehlsdaten an einem bestimmten Offset. Auf diese Weise können wir Befehlsdaten wie den Token-Betrag und den SOL-Betrag abrufen.getInstructionData(): Extrahiert den Betrag und den SOL-Betrag aus den Befehlsdaten auf der Grundlage bekannter Offsets aus der IDL des Programms.getTransactionType(): Ermittelt anhand des Diskriminators in den Auftragsdaten und der in unserer Konfiguration hinterlegten Diskriminatoren (bekannte Werte aus der IDL des Programms), ob es sich bei der Transaktion um einen Kauf oder einen Verkauf handelt.matchesInstructionDiscriminator(): Prüft, ob eine Anweisung dem Kauf- oder Verkaufsdiskriminator entspricht.
Unseren Bot initialisieren
Zuletzt erstellen wir eine Methode zum Initialisieren unserergRPC und eine weitere zum Starten unseres Bots. Fügen Sie die folgenden Methoden zu Ihrer CopyTradeBot- Klasse hinzu:
monitorWhales = async () => {
console.log("Monitoring whales...");
const client = new Client(
process.env.YELLOWSTONE_ENDPOINT,
process.env.YELLOWSTONE_TOKEN,
{}
);
const stream = await client.subscribe();
const request = this.createSubscribeRequest();
try {
await this.sendSubscribeRequest(stream, request);
console.log(
"Geyser connection established - watching whale Pump.fun activity."
);
await this.handleStreamEvents(stream);
} catch (error) {
console.error("Error in subscription process:", error);
stream.end();
}
};
start = async () => {
console.log("🤖 Pump.fun Copy Trading Bot Starting...");
this.monitorWhales();
};
Und das bewirken diese Methoden:
monitorWhales(): Richtet einengRPC ein, wobei der endpoint das Token verwendet werden, die Sie in Ihrer.env. Anschließend richtet es unser Abonnement für die für uns relevanten Transaktionen von Pump.fun ein und wartet auf eingehende Daten. Schließlich werden die Streaming-Daten anhandleStreamEventszur Bearbeitung.start(): Startet den Bot durch Aufruf vonmonitorWhales(). Dies ist der Einstiegspunkt für unseren Bot.
Fügen wir einfach noch eine letzte Zeile zu unserem bot.js Datei, die unseren Bot startet, sobald das Skript ausgeführt wird. Füge diese Hauptfunktion hinzu draußen der CopyTradeBot Klasse:
async function main() {
const bot = new CopyTradeBot();
await bot.start();
}
main().catch(console.error);
Das war’s! Falls du Probleme hattest, findest du den vollständigen Code für dieses Projekt in unserem GitHub-Beispiel-Repository hier. Probieren wir es doch mal aus!
Den Bot testen
An dieser Stelle ist Ihr bot.js Die Datei enthält die gesamte Logik zur Überwachung einer oder mehrerer Ziel-Wallets (BEOBACHTUNGSLISTE) und das Kopieren ihrer Pump.fun-Käufe, falls die SOL-Beträge Ihre MIN_TX_AMOUNT. Stellen Sie zu Demonstrations- bzw. Testzwecken sicher, dass Ihre BEOBACHTUNGSLISTE ist lediglich eine einzelne Adresse einer Wallet, über die Sie die Kontrolle haben. Diese werden wir im nächsten Schritt verwenden, um unsere eigenen Trades zu kopieren.
Dieses Beispiel läuft im mainnet führt echte Trades aus, wenn Sie TEST_MODUS zu false . Seien Sie vorsichtig beim Einsatz von echtem Geld und ziehen Sie in Betracht, zunächst auf einem lokales Netzwerk oder durch die Verwendung kleiner SOL-Beträge. Transaktionen auf der Blockchain sind unwiderruflich und können bei unsachgemäßer Ausführung zu einem Verlust von Guthaben führen.
- Geld auf Ihr Bot-Wallet einzahlen: Stellen Sie sicher, dass Ihr Bot-Wallet (das in Ihrer .env-Datei) über genügend SOL verfügt, um etwaige Swap-Gebühren und Handelsgeschäfte abzudecken.
- Den Bot ausführen:
node bot.js
- Einen Handel auslösen: Verwenden Sie dasselbe Wallet aus Ihrem
BEOBACHTUNGSLISTE, dann schau doch mal bei Pump.fun und eine Kauftransaktion durchführen. - Bot-Ausgabe prüfen:
- Auf Ihrer Konsole sollten Meldungen wie 🎯 – BUY – TxID: ... angezeigt werden.
- Wenn die Transaktion erkannt wird, sollte in der Datei „pump_fun_swaps.json“ ein Protokolleintrag „COPY_BUY“ zu sehen sein.
- Wenn du den Bot mit
TEST_MODUSauf … einstellenfalse, können Sie Ihretxidauf Solana um die Transaktionsdetails anzuzeigen.

Tolle Leistung!
Weiter so!
Herzlichen Glückwunsch! Sie haben einen einfachen, aber leistungsstarken Solana entwickelt, der die Pump.fun-API für Swaps und Solana gRPC das Echtzeit-Transaktions-Streaming nutzt. Diese Konfiguration veranschaulicht, wie Sie On-Chain-Ereignisse abonnieren und programmgesteuert darauf reagieren können, indem Sie über die Pump.fun-API Ihre eigenen Swap-Transaktionen abrufen und senden.
Du kannst diesen Bot gerne an komplexere Strategien anpassen:
- Variable Kaufbeträge: Anstelle eines festen BUY_AMOUNT können Sie das Verhältnis zwischen dem Kaufvolumen des „Whales“ und Ihrem insgesamt verfügbaren SOL-Guthaben verfolgen.
- Stop-Loss-/Verkaufssignale: Implementieren Sie eine Logik zur Erkennung großer Verkaufswellen oder legen Sie Schwellenwerte für den automatischen Verkauf der Token fest.
- Überwachung mehrerer Programme: Erweitern Sie IhrgRPC , um auch andere DeFi-Protokolle auf Solana zu überwachen. Ziehen Sie in Betracht, die Metis-API für andere DEXes oder AMMs zu nutzen und/oder Limit-Orders einzurichten.
Vielen Dank fürs Mitlesen und viel Spaß beim Aufbau! Wenn Sie Fragen haben oder weitere Tutorials Solana DeFi entdecken möchten, schauen Sie sich unsere Anleitungen und die Dokumentation an. Viel Erfolg und handeln Sie sicher!
Teilt uns mit, was ihr gerade so macht, oder wenn ihr Fragen habt! Ihr könnt uns auf Discord oder Twitter erreichen.
