24 Minuten Lesezeit
Ihr Quicknode-Stream kann nun über eine einzige Pipeline gleichzeitig an mehrere Ziele übertragen. Keine doppelten Streams, keine Abweichungen in der Konfiguration und keine zusätzlichen Kosten pro Ziel. Die pro Stream verfügbaren Ziele variieren je nach Tarif. Erfahren Sie mehr.
Übersicht
Die Beobachtung großer Token-Bewegungen in der Blockchain – oft als „Whale“-Aktivitäten bezeichnet – kann wertvolle Einblicke in die Marktstimmung und mögliche Kursentwicklungen liefern.
Diese Anleitung führt Sie Schritt für Schritt durch die Erstellung eines Echtzeit-Whale-Alert-Bots für den HYPE-Token auf der Hyperliquid-Blockchain. Sie werden ein System entwickeln, das nicht nur große Transfers erkennt, sondern die Daten auch mit aktuellen USD-Kursen von HyperCore anreichert und sofortige Benachrichtigungen an einen Telegram-Kanal sendet. Dazu nutzen wir die Leistungsfähigkeit und Effizienz von Quicknode Streams.
Ihre Aufgaben
- Erstellen Sie ein Quicknode-Stream das den HYPE herausfiltert
ÜbertragungEreignisse von Hyperliquid EVM - Überprüfen Sie die Echtheit der Nutzdaten vor der Verarbeitung anhand einer HMAC-Signatur.
- Lies den HYPE-Spotpreis von HyperCore über HyperEVM-Precompiles aus
- Stufenweise Wal-Benachrichtigungen (Fisch, Delfin, Wal) an einen Telegram-Kanal senden
Was Sie benötigen
- Ein Quicknode-Konto mit einem Hyperliquid-EVM-Endpunkt
- Node.js 20+, npm (oder ein anderer Paketmanager) und ein Code-Editor wie VS Code
- Ein Telegram-Konto (falls Sie einen Telegram-Bot erstellen möchten)
- Grundkenntnisse in JavaScript
- Ein Tool, mit dem Sie Ihren lokalen Server für das Internet zugänglich machen können, wie beispielsweise ngrok oder localtunnel (falls Sie Webhooks lokal testen müssen)
Quicknode Streams arbeiten nach einem „Push“-Modell. Anstatt dass Sie die Blockchain wiederholt nach neuen Daten abfragen (Polling), überwachen Streams die Blockchain für Sie und leiten die relevanten Daten an Ihre Anwendung weiter, sobald ein Ereignis eintritt.
Dieser Ansatz ist äußerst effizient und bietet mehrere wesentliche Vorteile:
- Echtzeitdaten: Erhalten Sie Benachrichtigungen sofort, ohne die Verzögerung durch Abfragezyklen.
- Geringerer Verwaltungsaufwand: Sie müssen sich nicht mehr um die Verwaltung einer komplexen und ressourcenintensiven Polling-Infrastruktur kümmern.
- Leistungsstarke Filterung: Daten werden auf der Seite von Quicknode verarbeitet und gefiltert, sodass Ihr Server nur genau die Informationen erhält, die er benötigt.
- Kosteneffizient: Sie zahlen nur pro übermitteltem Ereignis, was diese Lösung zu einer budgetfreundlichen Option für die Echtzeit-Datenüberwachung macht.
Das „Whale Alert Bot“-Projekt
Der „Whale Alert“-Bot besteht aus mehreren miteinander verbundenen Komponenten, die zusammenarbeiten, um Benachrichtigungen in Echtzeit zu übermitteln:
-
Hyperliquid-Blockchain: A
ÜbertragungVeranstaltung (Transfer(Adresse, Adresse, uint256)) für den HYPE-Token wird bei jeder Übertragung ausgegeben. -
Quicknode-Streams mit Filterung: Der Stream überwacht die Blockchain ständig und erfasst dieses Ereignis auf Grundlage der von uns definierten Filterfunktion.
-
Webhook-Übermittlung: Quicknode sendet die gefilterten Daten über eine sichere POST-Anfrage an unseren Server-Endpunkt.
-
Node.js-Server: Unser Server empfängt die Daten, überprüft deren Echtheit anhand des Sicherheitstokens des Webhooks und verarbeitet sie.
-
Preisabfrage: Der Server ruft den HyperCore-Precompile-Vertrag auf Hyperliquid auf, um den aktuellen USD-Preis von HYPE abzurufen.
-
Telegram-Bot: Schließlich formatiert der Server eine aussagekräftige, gut lesbare Nachricht und sendet die Benachrichtigung über die Telegram-Bot-API an unseren dafür vorgesehenen Kanal.

Dies ist der gesamte Ereignisablauf, den wir umsetzen werden. Nun wollen wir mit der Entwicklung des Hyperliquid-Whale-Alert-Bots beginnen.
Schritt 1: Erstelle deinen Telegram-Bot und deinen Kanal
Zunächst benötigst du einen Telegram-Bot und einen Kanal, in dem dieser Benachrichtigungen veröffentlichen kann.
Einen Bot mit BotFather erstellen
- Öffne Telegram und suche nach dem BotFather.
- Starte einen Chat mit BotFather und gib den Befehl ein
/newbotum einen neuen Bot zu erstellen. - Befolgen Sie die Anweisungen, um einen Namen und einen Benutzernamen für Ihren Bot festzulegen.
- BotFather stellt dir ein Bot-Token zur Verfügung. Bewahre dieses Token sicher auf; du benötigst es für deine
.envDatei.
Kanal erstellen
- Erstelle in Telegram einen neuen Kanal. Du kannst ihn öffentlich oder privat einrichten.
- Vergeben Sie für einen öffentlichen Kanal einen einprägsamen Benutzernamen (z. B. @hyperliquid_whales). Dieser Benutzername ist Ihr
TELEGRAM_CHANNEL_ID. - Für einen privaten Kanal benötigst du dessen numerische Chat-ID. Diese erhältst du, indem du eine Nachricht aus dem Kanal an einen Bot wie
@JsonDumpCUBot, und die dort angegebene Chat-ID zu überprüfen (d. h.,forward_from_chat.id).
Füge deinen Bot dem Kanal hinzu
- Öffnen Sie die Einstellungen Ihres neu erstellten Kanals.
- Füge deinen Bot hinzu und ernenne ihn zum Administrator.
Du hast nun dein TELEGRAM_BOT_TOKEN und dein TELEGRAM_CHANNEL_ID.
Schritt 2: Erstellen Sie Ihren Quicknode Hyperliquid EVM-Endpunkt
Erstellen Sie nun Ihren Quicknode Hyperliquid EVM-Endpunkt, über den Sie mit dem Hyperliquid Core interagieren werden, um HYPE-Kursdaten abzurufen.
Zunächst benötigen Sie ein Quicknode-Konto. Falls Sie bereits eines haben, melden Sie sich einfach an. Sobald Sie sich auf Ihrem Quicknode-Dashboard befinden:
- Wechseln Sie zur Seite „Endpunkte“
- Klicken Sie auf die Schaltfläche „Neuer Endpunkt“
- Wählen Sie das Hyperliquid-EVM-Mainnet -Netzwerk aus
- Erstellen Sie Ihren Endpunkt
Nachdem Ihr Endpunkt erstellt wurde, kopieren Sie die Endpunkt-URL und halten Sie sie griffbereit. Sie müssen sie zu Ihrer .env Datei in einem späteren Schritt.
Schritt 3: Erstellen Sie Ihren Quicknode-Stream
Nun richten wir den Quicknode-Stream ein, der die Hyperliquid-Blockchain überwachen wird.
Den Stream erstellen
- Rufen Sie das Quicknode-Dashboard auf und wechseln Sie zum Abschnitt „Streams “.
- Klicken Sie auf „Stream erstellen“ und wählen Sie „Hyperliquid EVM Mainnet “ als Blockchain aus.
- Wählen Sie „Block mit Belegen “ als Datensatz aus
- Klicken Sie auf „Payload anpassen“, um einen Filter anzuwenden, und wählen Sie die Option „Eigenen Filter erstellen “ aus.
Filter festlegen
In dieser Anleitung erstellen wir eine benutzerdefinierte JavaScript-Funktion, um unsere mehrstufige Warnlogik zu implementieren.
Die Funktion, die wir verwenden werden, überprüft jede Transaktion in jedem neuen Block. Sie sucht gezielt nach Ereignisprotokollen, die dem ERC20-Standard entsprechen. Übertragung Unterschrift (0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef) und stammen aus dem HYPE Token-Vertrag. Für einen Übertragung Bei einem Ereignis enthält das Array „topics“ den Absender und den Empfänger, während log.data enthält den Betrag. Unser Code entschlüsselt diese Informationen, vergleicht den Betrag mit unseren Schwellenwerten und gibt nur dann eine bereinigte Datenlast zurück, wenn die Überweisung groß genug ist. Diese Vorverarbeitung ist unglaublich effizient und stellt sicher, dass unser Server keine Ressourcen für irrelevante Daten verschwendet.
Fügen Sie den folgenden Code in das Funktionsfeld ein:
// Quicknode Stream Filter for Tiered Wrapped HYPE Token Transfers
function main(payload) {
// --- Configuration ---
// The specific token contract address for Wrapped HYPE
const WHYPE_ADDRESS = "0x5555555555555555555555555555555555555555";
// Define the thresholds for each tier (with 18 decimals)
const TIER_THRESHOLDS = {
whale: BigInt("10000000000000000000000"), // 10,000 HYPE
dolphin: BigInt("5000000000000000000000"), // 5,000 HYPE
small_fish: BigInt("1000000000000000000000"), // 1,000 HYPE
};
// --- Static Data ---
const TRANSFER_SIGNATURE =
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
const { data } = payload;
const block = data[0].block;
const receipts = data[0].receipts || [];
const categorizedTransfers = [];
const blockNumber = parseInt(block.number, 16);
const blockTimestamp = parseInt(block.timestamp, 16);
for (const receipt of receipts) {
for (const log of receipt.logs || []) {
// Primary filter: Is this a Transfer event from the W-HYPE contract?
if (
log.address.toLowerCase() === WHYPE_ADDRESS &&
log.topics[0] === TRANSFER_SIGNATURE
) {
const transferValue = BigInt(log.data);
let tier = null;
// Tiering Logic: Check from the highest threshold down to the lowest.
if (transferValue >= TIER_THRESHOLDS.whale) {
tier = "whale";
} else if (transferValue >= TIER_THRESHOLDS.dolphin) {
tier = "dolphin";
} else if (transferValue >= TIER_THRESHOLDS.small_fish) {
tier = "small_fish";
}
// If the transfer meets any of our thresholds, process it.
if (tier) {
const fromAddress = "0x" + log.topics[1].slice(26);
const toAddress = "0x" + log.topics[2].slice(26);
categorizedTransfers.push({
tier: tier,
tokenContract: log.address,
from: fromAddress,
to: toAddress,
value: transferValue.toString(),
transactionHash: receipt.transactionHash,
blockNumber: blockNumber,
timestamp: blockTimestamp,
});
}
}
}
}
if (categorizedTransfers.length > 0) {
return {
largeTransfers: categorizedTransfers,
};
}
return null;
}
Testen Sie Ihren Filter
Wählen Sie einen Block aus (z. B. 12193297), um Ihre Filterbedingungen zu testen und sicherzustellen, dass die Warnmeldungen korrekt ausgelöst werden. Sie sollten eine Nutzlast sehen, die die kategorisierten Überweisungen wie folgt enthält:
{
"largeTransfers": [
{
"blockNumber": 12193297,
"from": "0x7c97cd7b57b736c6ad74fae97c0e21e856251dcf",
"tier": "small_fish",
"timestamp": 1756245832,
"to": "0xaaa2851ec59f335c8c6b4db6738c94fd0305598a",
"tokenContract": "0x5555555555555555555555555555555555555555",
"transactionHash": "0xafe522067fca99d4b44030d82885cabb757943255b991b3f2e95564807dbe0f7",
"value": "2200000000000000000000"
}
]
}
Sicherheitstoken abrufen und die Webhook-URL festlegen
Sobald Sie zum nächsten Schritt übergehen, werden Sie aufgefordert, Ihr Ziel auszuwählen. Wählen Sie Webhook als Zieltyp. Anschließend generiert Quicknode automatisch eine Sicherheitstoken um zu überprüfen, ob eingehende Anfragen authentisch sind. Kopieren Sie dieses Token in Ihre .env Datei als Wert des WEBHOOK_SECRET Variable.
Für die Ziel-URL des Streams benötigen Sie einen öffentlich zugänglichen Endpunkt. Während der Entwicklung können Sie ngrok oder localtunnel um Ihren lokalen Server nach außen zugänglich zu machen. Sie können Folgendes ausführen: ngrok http 3000 (vorausgesetzt, Ihr Server läuft auf Port 3000) und kopieren Sie die HTTPS-Weiterleitungs-URL, sobald Ihr Server läuft. Denken Sie daran, Folgendes anzuhängen: /webhook dazu (z. B. https://your-ngrok-id.ngrok.io/webhook), da Sie dort Ihren Webhook-Listener erstellen werden.
Da unser Server noch nicht eingerichtet ist, machen wir hier eine Pause und kehren nach der Einrichtung des Servers zurück, um den Stream zu testen und zu aktivieren.
Schritt 4: Einrichten des Webhook-Servers
Nun erstellen wir die Node.js-Anwendung, die die Daten von unserem Webhook empfangen und verarbeiten wird.
Projekteinrichtung und Abhängigkeiten
Erstellen Sie zunächst ein Verzeichnis für Ihr Projekt und installieren Sie die erforderlichen Abhängigkeiten. Sie können dazu npm oder einen anderen Paketmanager (wie beispielsweise Garn, pnpm, Brötchen), um dies zu tun. Zum Beispiel:
mkdir hyperliquid-whale-alert-bot && cd hyperliquid-whale-alert-bot
npm init -y
npm i express dotenv node-telegram-bot-api viem
npm i -D nodemon
Aktualisiere anschließend deine package.json um Folgendes aufzunehmen:
"type": "module",
"scripts": {
"start": "node index.js",
"dev": "nodemon index.js"
}
Projektstruktur
Erstellen Sie die folgenden Dateien, um eine Grundstruktur für Ihr Projekt zu schaffen:
├── config.js // Konfigurationseinstellungen
├── index.js // Haupteinstiegspunkt
├── priceService.js // Preisbezogene Logik
├── security.js // Sicherheitslogik
├── telegramService.js // Telegram-Bot-Integration
└── .env // Umgebungsvariablen
└── .gitignore // Von Git ignorierte Dateien Datei
Hier ist der Einzeiler, den Sie in Ihrem Terminal ausführen können, um all diese Dateien sofort zu erstellen:
touch config.js index.js priceService.js security.js telegramService.js .env .gitignore
# Falls der Befehl „touch“ nicht verfügbar ist, können Sie Folgendes verwenden:
# (echo > config.js) && (echo > index.js) && (echo > priceService.js) && (echo > security.js) && (echo > telegramService.js) && (echo > .env) && (echo > .gitignore)
Umgebungsvariablen
Aktualisieren Sie die .env Datei im Stammverzeichnis Ihres Projekts, um Ihre Umgebungsvariablen zu speichern. Fügen Sie die folgenden Variablen hinzu:
# Telegram-Konfiguration
TELEGRAM_BOT_TOKEN=Ihr_Telegram-Bot-Token
TELEGRAM_CHANNEL_ID=Ihre_Kanal-ID
# Serverkonfiguration
PORT=3000
# Sicherheit der Streams-Webhooks
WEBHOOK_SECRET=Ihr_optionales_Webhook-Geheimnis
# Quicknode-Konfiguration für Hyperliquid EVM RPC
HYPERLIQUID_RPC=https://your-endpoint.quiknode.pro/your-token/
# Umgebung
NODE_ENV=Entwicklung
Code-Implementierung
.gitignore
Es ist wichtig, ein .gitignore Datei in Ihr Projekt ein, um zu vermeiden, dass vertrauliche Informationen und unnötige Dateien committet werden. Erstellen Sie eine .gitignore Datei im Stammverzeichnis Ihres Projekts und fügen Sie die folgenden Zeilen hinzu:
node_modules
.env
config.js
Diese Datei enthält die Konfigurationseinstellungen für Ihre Anwendung, einschließlich Umgebungsvariablen und anderer Konstanten.
Die Punkt price precompile befindet sich unter 0x...0808, während die Orakel Der Preis für die Vorkompilierung beträgt 0x...0807. Je nach Preisquelle können Sie beides verwenden; in dieser Anleitung wird Punkt. Überprüfen Sie Adressen stets anhand offizieller Dokumente und aktueller Leitfäden.
import dotenv from "dotenv";
// Load environment variables
dotenv.config();
export const TIERS = {
whale: {
emoji: "🐋",
label: "WHALE",
},
dolphin: {
emoji: "🐬",
label: "DOLPHIN",
},
small_fish: {
emoji: "🐟",
label: "FISH",
},
};
export const EXPLORER = {
tx: "https://hypurrscan.io/tx/",
address: "https://hypurrscan.io/address/",
block: "https://hypurrscan.io/block/",
};
export const HYPERCORE = {
SPOT_PX_PRECOMPILE: "0x0000000000000000000000000000000000000808",
HYPE_SPOT_INDEX: 107, // Mainnet HYPE spot ID
RPC_URL: process.env.HYPERLIQUID_RPC || "https://api.hyperliquid.xyz/evm",
};
export const MESSAGE_DELAY_MS = 1000; // Delay between Telegram messages
export const PORT = process.env.PORT || 3000;
priceService.js
Dieser Dienst ist dafür zuständig, den HYPE-Kurs von HyperCore abzurufen.
Wir werden das SPOT price wird direkt mit einem 32-Byte-ABI-kodierten Index vorkompiliert. Die Vorkompilierung gibt einen uint64 wobei die Dezimalskalierung von der des Vermögenswerts abhängt szDecimals (Das Preissystem von Hyperliquid).
// priceService.js
// Fetches HYPE price from HyperCore using precompile
import { createPublicClient, http, encodeAbiParameters, formatUnits } from "viem";
import { HYPERCORE } from "./config.js";
// Create viem client for HyperEVM
const client = createPublicClient({
transport: http(HYPERCORE.RPC_URL),
});
// Cache price for 30 seconds to avoid excessive RPC calls
let priceCache = {
price: null,
timestamp: 0,
};
const CACHE_DURATION = 30000; // 30 seconds
/**
* Fetches HYPE spot price from HyperCore precompile
* Price is returned with 6 decimals precision for HYPE
* Details: To convert to floating point numbers, divide the returned price by 10^(8 - base asset szDecimals) for spot
* Source: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/hyperevm/interacting-with-hypercore
* @returns {Promise<number|null>} HYPE price in USD
*/
export async function getHypePrice() {
try {
// Check cache first
if (
priceCache.price &&
Date.now() - priceCache.timestamp < CACHE_DURATION
) {
console.log("Using cached HYPE price:", priceCache.price);
return priceCache.price;
}
// Encode the spot index as a uint32 parameter
const encodedIndex = encodeAbiParameters(
[{ name: "index", type: "uint32" }],
[HYPERCORE.HYPE_SPOT_INDEX]
);
// Call the spot price precompile
const result = await client.call({
to: HYPERCORE.SPOT_PX_PRECOMPILE,
data: encodedIndex,
});
// szDecimals for HYPE is 2.
const szDecimals = 2;
const priceRaw = BigInt(result.data);
const price = formatUnits(priceRaw, 8 - szDecimals); // Convert to decimal string
// Update cache
priceCache = {
price,
timestamp: Date.now(),
};
console.log(`Fetched HYPE price from HyperCore: $${price}`);
return price;
} catch (error) {
console.error("Error fetching HYPE price from HyperCore:", error);
// Return cached price if available, otherwise null
return priceCache.price || null;
}
}
/**
* Formats USD value based on HYPE amount and price
* @param {string} hypeAmount - HYPE amount as string
* @param {number} hypePrice - HYPE price in USD
* @returns {string} Formatted USD value
*/
export function formatUSD(hypeAmount, hypePrice) {
if (!hypePrice) return "";
const usdValue = parseFloat(hypeAmount) * hypePrice;
return `($${usdValue.toLocaleString("en-US", {
minimumFractionDigits: 0,
maximumFractionDigits: 0,
})})`;
}
security.js
Dieses Modul enthält die Logik zur Validierung eingehender Webhooks. Quicknode empfiehlt die HMAC-Überprüfung mithilfe von Headern. X-QN-Nonce, X-QN-Zeitstempel, und X-QN-Signatur Und dieses Modul führt diese Überprüfung durch.
Weitere Informationen finden Sie im Leitfaden „Validierung von Stream-Signaturen “.
// security.js
// Validates incoming webhook signatures from Quicknode
import crypto from "crypto";
/**
* Validates the webhook signature from Quicknode
* Based on Quicknode's HMAC-SHA256 signature validation
*
* @param {string} secretKey - The webhook secret key
* @param {string} payload - The request body as string
* @param {string} nonce - The nonce from headers
* @param {string} timestamp - The timestamp from headers
* @param {string} givenSignature - The signature from headers
* @returns {boolean} Whether the signature is valid
*/
export function validateWebhookSignature(
secretKey,
payload,
nonce,
timestamp,
givenSignature
) {
if (!secretKey || !nonce || !timestamp || !givenSignature) {
console.warn("⚠️ Missing required parameters for signature validation");
return false;
}
try {
// Concatenate nonce + timestamp + payload as strings
const signatureData = nonce + timestamp + payload;
// Convert to bytes
const signatureBytes = Buffer.from(signatureData);
// Create HMAC with secret key converted to bytes
const hmac = crypto.createHmac("sha256", Buffer.from(secretKey));
hmac.update(signatureBytes);
const computedSignature = hmac.digest("hex");
// Use timing-safe comparison to prevent timing attacks
const isValid = crypto.timingSafeEqual(
Buffer.from(computedSignature, "hex"),
Buffer.from(givenSignature, "hex")
);
if (isValid) {
console.log("✅ Webhook signature validated successfully");
} else {
console.error("❌ Invalid webhook signature");
}
return isValid;
} catch (error) {
console.error("Error validating webhook signature:", error);
return false;
}
}
/**
* Middleware for Express to validate webhook signatures
* Quicknode sends nonce, timestamp, and signature in headers
*/
export function webhookAuthMiddleware(req, res, next) {
// Skip validation if no secret is configured
const secretKey = process.env.WEBHOOK_SECRET;
if (!secretKey) {
console.log("ℹ️ Webhook secret not configured, skipping validation");
return next();
}
// Get Quicknode headers
const nonce = req.headers["x-qn-nonce"];
const timestamp = req.headers["x-qn-timestamp"];
const givenSignature = req.headers["x-qn-signature"];
if (!nonce || !timestamp || !givenSignature) {
console.error("🚫 Missing required Quicknode headers");
return res.status(400).json({
error: "Missing required headers",
message:
"x-qn-nonce, x-qn-timestamp, and x-qn-signature headers are required",
});
}
// Get the raw body as string
// Note: Express's JSON middleware already parsed the body, so we need to stringify it back
const payloadString = JSON.stringify(req.body);
// Validate the signature
const isValid = validateWebhookSignature(
secretKey,
payloadString,
nonce,
timestamp,
givenSignature
);
if (!isValid) {
console.error("🚫 Webhook validation failed");
return res.status(401).json({
error: "Invalid signature",
message: "The webhook signature could not be validated",
});
}
next();
}
telegramService.js
Dieses Modul formatiert die endgültige Nachricht und sendet sie an Ihren Telegram-Kanal.
// telegramService.js
// Handles Telegram bot messaging
import TelegramBot from "node-telegram-bot-api";
import { formatEther } from "viem";
import { TIERS, EXPLORER, MESSAGE_DELAY_MS } from "./config.js";
import { getHypePrice, formatUSD } from "./priceService.js";
// Initialize Telegram bot
const bot = new TelegramBot(process.env.TELEGRAM_BOT_TOKEN, { polling: false });
const CHANNEL_ID = process.env.TELEGRAM_CHANNEL_ID;
/**
* Format an address for display
*/
function formatAddress(address) {
return `${address.slice(0, 6)}...${address.slice(-4)}`;
}
/**
* Format transaction hash for display
*/
function formatTxHash(hash) {
return `${hash.slice(0, 10)}...`;
}
/**
* Display time
*/
function getTime(timestamp) {
const date = new Date(timestamp * 1000);
return date.toLocaleString("en-US");
}
/**
* Create formatted Telegram message for a transfer
*/
async function createMessage(transfer) {
const tierConfig = TIERS[transfer.tier];
const formattedValue = formatEther(BigInt(transfer.value));
const hypePrice = await getHypePrice();
const usdValue = formatUSD(formattedValue, hypePrice);
// Create message with Markdown formatting
const message = `
${tierConfig.emoji} *${tierConfig.label} ALERT* ${tierConfig.emoji}
💰 *Amount:* \`${parseFloat(formattedValue).toLocaleString("en-US", {
maximumFractionDigits: 2,
})} HYPE\` ${usdValue}
📤 *From:* [${formatAddress(transfer.from)}](${EXPLORER.address}${
transfer.from
})
📥 *To:* [${formatAddress(transfer.to)}](${EXPLORER.address}${transfer.to})
🔗 *TX:* [${formatTxHash(transfer.transactionHash)}](${EXPLORER.tx}${
transfer.transactionHash
})
📦 *Block:* [#${transfer.blockNumber}](${EXPLORER.block}${transfer.blockNumber})
⏰ *Time:* ${getTime(transfer.timestamp)}
Powered by [Hyperliquid](https://hyperliquid.xyz) & [Quicknode Streams](https://www.quicknode.com/streams)`;
return message;
}
/**
* Send message to Telegram with retry logic
*/
export async function sendMessage(message, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
await bot.sendMessage(CHANNEL_ID, message, {
parse_mode: "Markdown",
disable_web_page_preview: true,
});
console.log("✅ Message sent to Telegram successfully");
return true;
} catch (error) {
console.error(`❌ Telegram send attempt ${i + 1} failed:`, error.message);
if (i < retries - 1) {
// Wait before retrying (exponential backoff)
await new Promise((resolve) =>
setTimeout(resolve, 1000 * Math.pow(2, i))
);
}
}
}
return false;
}
/**
* Process and send alerts to Telegram
*/
export async function processAlerts(transfers) {
console.log(`📨 Processing ${transfers.length} transfers for Telegram...`);
for (const transfer of transfers) {
const message = await createMessage(transfer);
const sent = await sendMessage(message);
if (!sent) {
console.error(
"Failed to send message for transfer:",
transfer.transactionHash
);
}
// Rate limiting between messages
if (transfers.indexOf(transfer) < transfers.length - 1) {
await new Promise((resolve) => setTimeout(resolve, MESSAGE_DELAY_MS));
}
}
// Send summary if there are multiple transfers
if (transfers.length > 3) {
const summaryMessage = `
📊 *Batch Summary*
Total transfers: ${transfers.length}
🐋 Whales: ${transfers.filter((t) => t.tier === "whale").length}
🐬 Dolphins: ${transfers.filter((t) => t.tier === "dolphin").length}
🐟 Fish: ${transfers.filter((t) => t.tier === "small_fish").length}
Block: #${transfers[0].blockNumber}
`;
await sendMessage(summaryMessage);
}
}
index.js
Dies ist die Hauptdatei, die alles miteinander verknüpft.
Wir verwenden einen Express-Raw-Body-Parser vor jeglicher JSON-Middleware für die /webhook Endpunkt, um Signaturen überprüfen zu können. Dadurch wird sichergestellt, dass der Textkörper in seiner ursprünglichen Form für die HMAC-Überprüfung verfügbar ist.
// server.js
// Main webhook server for Hyperliquid whale alerts
import express from "express";
import dotenv from "dotenv";
import { PORT, HYPERCORE } from "./config.js";
import { processAlerts } from "./telegramService.js";
import { webhookAuthMiddleware } from "./security.js";
// Load environment variables
dotenv.config();
// Initialize Express app
const app = express();
// Custom middleware to capture raw body for signature validation
app.use((req, res, next) => {
if (req.path === '/webhook' && process.env.WEBHOOK_SECRET) {
// For webhook endpoint with security enabled, capture raw body
let rawBody = '';
req.setEncoding('utf8');
req.on('data', (chunk) => {
rawBody += chunk;
});
req.on('end', () => {
req.rawBody = rawBody;
// Parse JSON body
try {
req.body = JSON.parse(rawBody);
} catch (error) {
return res.status(400).json({ error: 'Invalid JSON payload' });
}
next();
});
} else {
// For other endpoints or when security is disabled, use normal JSON parsing
express.json()(req, res, next);
}
});
// Main webhook endpoint with security validation
app.post("/webhook", webhookAuthMiddleware, async (req, res) => {
try {
console.log("📨 Webhook received at", new Date().toISOString());
const { largeTransfers } = req.body;
if (!largeTransfers || largeTransfers.length === 0) {
console.log("No large transfers in this webhook");
return res.json({ success: true, processed: 0 });
}
console.log(`Processing ${largeTransfers.length} transfers...`);
// Send alerts to Telegram
await processAlerts(largeTransfers);
console.log(`✅ Processed ${largeTransfers.length} transfers successfully`);
res.json({
success: true,
processed: largeTransfers.length,
});
} catch (error) {
console.error("❌ Webhook processing error:", error);
res.status(500).json({
success: false,
error: error.message,
});
}
});
// Health check endpoint
app.get("/health", (req, res) => {
res.json({
status: "healthy",
timestamp: new Date().toISOString(),
environment: {
telegramConfigured: !!process.env.TELEGRAM_BOT_TOKEN,
webhookSecretConfigured: !!process.env.WEBHOOK_SECRET,
},
});
});
// Error handling middleware
app.use((error, req, res, next) => {
console.error("Unhandled error:", error);
res.status(500).json({
success: false,
error: "Internal server error",
});
});
// Start server
app.listen(PORT, () => {
console.log(`
╔════════════════════════════════════════════╗
║ Hyperliquid Whale Alert Server Started ║
╠════════════════════════════════════════════╣
║ Port: ${PORT} ║
║ Webhook: http://localhost:${PORT}/webhook ║
║ Health: http://localhost:${PORT}/health ║
╚════════════════════════════════════════════╝
Configuration:
- Telegram Bot: ${
process.env.TELEGRAM_BOT_TOKEN ? "✅ Configured" : "❌ Not configured"
}
- Telegram Channel: ${process.env.TELEGRAM_CHANNEL_ID || "Not configured"}
- Webhook Secret: ${
process.env.WEBHOOK_SECRET
? "✅ Configured"
: "⚠️ Not configured (validation disabled)"
}
- HyperCore RPC: ${HYPERCORE.RPC_URL}
Ready to receive webhooks...
`);
});
// Graceful shutdown
process.on("SIGTERM", () => {
console.log("SIGTERM received, shutting down gracefully...");
process.exit(0);
});
process.on("SIGINT", () => {
console.log("SIGINT received, shutting down gracefully...");
process.exit(0);
});
Schritt 5: System starten und testen
Jetzt kannst du deinen Bot zum Leben erwecken.
Starten Sie Ihren Server
Führen Sie den folgenden Befehl in Ihrem Terminal aus, um den Server zu starten. nodemon sorgt dafür, dass der Server bei Dateiänderungen automatisch neu gestartet wird.
# Im Entwicklungsmodus mit nodemon starten
npm run dev
Machen Sie Ihren Localhost öffentlich zugänglich
Falls Sie dies noch nicht getan haben, öffnen Sie ein neues Terminalfenster und führen Sie folgenden Befehl aus: ngrok http 3000. Kopieren Sie die HTTPS-Weiterleitungs-URL.
Testen Sie Ihren Stream
- Kehren Sie zur Seite Ihres Webhooks im Quicknode-Dashboard zurück.
- Fügen Sie Ihre ngrok-URL ein (z. B.
https://your-ngrok-id.ngrok.io/webhook) in das Feld „Webhook-URL“ ein und speichern Sie die Eingabe. - Klicken Sie nun auf die Schaltfläche „Beispiel-Payload senden “. Quicknode sendet daraufhin eine Beispiel-Payload an Ihren laufenden Server.
- Überprüfen Sie die Konsolenprotokolle Ihres Servers und sehen Sie in Ihrem Telegram-Kanal nach, ob dort Benachrichtigungen vorliegen.
Hier ist ein Beispiel dafür, wie die endgültige Benachrichtigung aussehen wird:

Deinen Stream aktivieren
Sobald Sie sich vergewissert haben, dass alles funktioniert, klicken Sie auf die Schaltfläche „Stream erstellen“, um Ihren Stream zu erstellen. Ihr Bot ist nun live und überwacht alle neuen HYPE-Überweisungen in Echtzeit.
Fehlerbehebung
Manchmal, wenn Ihr Server oder ngrok Wenn das Programm nicht ordnungsgemäß beendet wird, kann beim Neustart möglicherweise eine Fehlermeldung wie „Adresse bereits in Gebrauch“ angezeigt werden. So können Sie das Problem schnell beheben.
Schritt 1: Den Port freigeben
Suchen Sie zunächst anhand des Ports die Prozess-ID (PID) und beenden Sie den Prozess anschließend. Die folgenden Befehle gelten für macOS/Linux; je nach Betriebssystem können sie abweichen.
# Find the Process ID (PID) using port 3004
lsof -i :3004
# Replace <PID> with the number you found and run:
kill -9 <PID>
Schritt 2: Neustart und Aktualisierung
Starten Sie Ihren Server neu und ngrok. Wichtig: ngrok Erzeugt bei jedem Start eine neue URL. Sie müssen diese neue URL kopieren und in Ihren Quicknode-Webhook-Einstellungen aktualisieren.
Fazit
Herzlichen Glückwunsch! Sie haben erfolgreich ein produktionsreifes Echtzeit-Warnsystem für die Hyperliquid-Blockchain entwickelt. Durch die Kombination der Leistungsfähigkeit von Quicknode Streams für On-Chain-Daten, eines sicheren Node.js-Servers für die Geschäftslogik und der Telegram-API für Benachrichtigungen haben Sie ein wertvolles Tool zur Überwachung des DeFi-Ökosystems geschaffen.
Dieses Muster ist flexibel: Sie können Ebenen erweitern, es mit zusätzlichem On-Chain-Kontext anreichern oder Datenquellen und -ziele austauschen, je nachdem, wie sich Ihr Anwendungsfall weiterentwickelt.
Nächste Schritte: Produktive Bereitstellung
Während ngrok eignet sich hervorragend für die Entwicklung, für eine Produktionsumgebung benötigen Sie jedoch eine dauerhaftere Lösung. Ziehen Sie in Betracht, Ihre Anwendung auf folgenden Plattformen bereitzustellen:
- Ein Virtual Private Server (VPS) wie DigitalOcean oder AWS, der mit einem Prozessmanager wie PM2 betrieben wird, um den Betrieb aufrechtzuerhalten
- Ein containerisierter Dienst unter Verwendung von Docker
- Eine Platform-as-a-Service (PaaS)-Plattform wie Heroku oder Render
Mögliche Verbesserungen
Dieses Projekt bietet eine solide Grundlage, auf der du aufbauen kannst. Hier sind ein paar Ideen, wie du deinen Bot auf die nächste Stufe bringen kannst:
-
Unterstützung mehrerer Token: Passen Sie den Code so an, dass er ein Array von Token-Adressen akzeptiert. Auf diese Weise können Sie mehrere Token mit unterschiedlichen Schwellenwerten überwachen und entsprechend Benachrichtigungen versenden.
-
Dashboard für historische Daten: Quicknode Streams unterstützt nicht nur Echtzeitdaten, sondern auch historische Daten. Speichern Sie die eingehenden Transaktionsdaten in einer Datenbank (z. B. PostgreSQL, MongoDB). Anschließend können Sie eine webbasierte dApp erstellen, um historische Trends zu visualisieren, den Nettofluss bestimmter „Whale“-Wallets zu verfolgen und tiefgreifendere On-Chain-Analysen durchzuführen.
-
Weitere Benachrichtigungskanäle hinzufügen: Integrieren Sie weitere Benachrichtigungsdienste wie Discord-Webhooks.
-
Auslöser für den automatisierten Handel: In einem fortgeschritteneren Anwendungsfall könnten Sie diese Benachrichtigungen nutzen, um On-Chain-Aktionen auszulösen. So könnte beispielsweise eine große Überweisung einen Swap auslösen.
Weitere Ressourcen
- Hyperliquid-Dokumentation
- Quicknode-Anleitungen zu Hyperliquid
- Quicknode Hyperliquid-Dokumentation
- Video: Was ist Hyperliquid HyperEVM und wie fängt man damit an?
- Video: So erstellen Sie ein Hyperliquid-Analytics-Dashboard
Wenn du nicht weiterkommst oder Fragen hast, teile sie uns einfach in unserem Discord-Server mit. Bleibe auf dem Laufenden, indem du uns auf X (ehemals Twitter) (@Quicknode) oder in unserem Telegram-Ankündigungskanal folgst.
Wir ❤️ Feedback!
Teilen Sie uns gerne mit, wenn Sie Feedback oder Vorschläge für neue Themen haben. Wir freuen uns auf Ihre Rückmeldung.
