Skip to main content

Documentation Index

Fetch the complete documentation index at: https://hypernode-docs.polynode.dev/llms.txt

Use this file to discover all available pages before exploring further.

Copy-paste patterns for the most common use cases. All examples use TypeScript with the ws package.

Whale alert bot

Detect large orders and send alerts.
import WebSocket from "ws";

const ws = new WebSocket("wss://hyper.polynode.dev/ws?key=pn_live_YOUR_KEY");
const THRESHOLD_USD = 100_000;

ws.on("open", () => {
  ws.send(JSON.stringify({
    action: "subscribe",
    filters: { action_types: ["order"] }
  }));
});

ws.on("message", (raw) => {
  const event = JSON.parse(raw.toString());
  if (event.type !== "order" || event.consensus !== "confirmed") return;

  const d = event.data;
  const notional = parseFloat(d.sz) * parseFloat(d.px);

  if (notional >= THRESHOLD_USD) {
    const side = d.side === "B" ? "BUY" : "SELL";
    console.log(`WHALE ${side} ${event.asset}: ${d.sz} @ ${d.px} ($${Math.round(notional).toLocaleString()})`);
    console.log(`  Wallet: ${event.user}`);
    console.log(`  Order ID: ${d.oid}`);
    // Send to Telegram, Discord, etc.
  }
});

Fill tracker with PnL

Track every fill on a wallet and compute running PnL.
import WebSocket from "ws";

const ws = new WebSocket("wss://hyper.polynode.dev/ws?key=pn_live_YOUR_KEY");
const WALLET = "0xd071d6d6ea52f5aa34b79e47f908ee48c8215837";
let totalPnl = 0;
let totalFees = 0;

ws.on("open", () => {
  ws.send(JSON.stringify({
    action: "subscribe",
    filters: { addresses: [WALLET], action_types: ["fill"] }
  }));
});

ws.on("message", (raw) => {
  const event = JSON.parse(raw.toString());
  if (event.type !== "fill") return;

  const d = event.data;
  const pnl = parseFloat(d.closedPnl || "0");
  const fee = parseFloat(d.fee || "0");

  totalPnl += pnl;
  totalFees += fee;

  console.log(`${event.asset} ${d.dir} ${d.sz}@${d.px}`);
  console.log(`  PnL: ${pnl >= 0 ? "+" : ""}${pnl.toFixed(2)} | Fee: ${fee.toFixed(4)} ${d.feeToken}`);
  console.log(`  Running: PnL=${totalPnl.toFixed(2)} Fees=${totalFees.toFixed(4)}`);
});

Cancel storm detector

Detect when market makers are aggressively pulling orders. A burst of cancels often precedes a price move.
import WebSocket from "ws";

const ws = new WebSocket("wss://hyper.polynode.dev/ws?key=pn_live_YOUR_KEY");
const WINDOW_MS = 2000;
const THRESHOLD = 20;

const cancelWindows: Map<string, number[]> = new Map();

ws.on("open", () => {
  ws.send(JSON.stringify({
    action: "subscribe",
    filters: { action_types: ["cancel", "cancelByCloid"], assets: ["BTC", "ETH", "SOL"] }
  }));
});

ws.on("message", (raw) => {
  const event = JSON.parse(raw.toString());
  if (event.consensus !== "pre") return;

  const asset = event.asset;
  const now = Date.now();

  if (!cancelWindows.has(asset)) cancelWindows.set(asset, []);
  const timestamps = cancelWindows.get(asset)!;
  timestamps.push(now);

  // Trim old timestamps
  while (timestamps.length > 0 && timestamps[0] < now - WINDOW_MS) {
    timestamps.shift();
  }

  if (timestamps.length >= THRESHOLD) {
    console.log(`CANCEL STORM: ${asset}${timestamps.length} cancels in ${WINDOW_MS}ms`);
    timestamps.length = 0; // Reset to avoid repeated alerts
  }
});

Copy trader

Replicate trades from a target wallet. Logs the signal; add your execution logic.
import WebSocket from "ws";

const ws = new WebSocket("wss://hyper.polynode.dev/ws?key=pn_live_YOUR_KEY");
const TARGETS = [
  "0xd071d6d6ea52f5aa34b79e47f908ee48c8215837",
  "0x1234567890abcdef1234567890abcdef12345678",
];

ws.on("open", () => {
  ws.send(JSON.stringify({
    action: "subscribe",
    filters: { addresses: TARGETS, action_types: ["fill"] }
  }));
});

ws.on("message", (raw) => {
  const event = JSON.parse(raw.toString());
  if (event.type !== "fill") return;

  const d = event.data;
  console.log(`COPY SIGNAL from ${event.user.slice(0, 8)}...`);
  console.log(`  ${event.asset} ${d.dir} ${d.sz} @ ${d.px}`);

  // Execute your copy trade:
  // POST to https://hyper.polynode.dev/exchange/order with your signed payload
});

Multi-asset dashboard

Stream orders and fills for multiple assets simultaneously.
import WebSocket from "ws";

const ws = new WebSocket("wss://hyper.polynode.dev/ws?key=pn_live_YOUR_KEY");
const ASSETS = ["BTC", "ETH", "SOL", "HYPE", "DOGE"];

const stats: Record<string, { orders: number; fills: number; volume: number }> = {};
for (const a of ASSETS) stats[a] = { orders: 0, fills: 0, volume: 0 };

ws.on("open", () => {
  for (const asset of ASSETS) {
    ws.send(JSON.stringify({
      action: "subscribe",
      filters: { assets: [asset], action_types: ["order", "fill"] }
    }));
  }
});

ws.on("message", (raw) => {
  const event = JSON.parse(raw.toString());
  if (!stats[event.asset]) return;

  if (event.type === "order") stats[event.asset].orders++;
  if (event.type === "fill") {
    stats[event.asset].fills++;
    stats[event.asset].volume += parseFloat(event.data.sz) * parseFloat(event.data.px);
  }
});

// Print stats every 10 seconds
setInterval(() => {
  console.clear();
  console.log("Asset     Orders   Fills    Volume");
  console.log("-------   ------   -----    ------");
  for (const [asset, s] of Object.entries(stats)) {
    console.log(`${asset.padEnd(10)}${String(s.orders).padEnd(9)}${String(s.fills).padEnd(9)}$${Math.round(s.volume).toLocaleString()}`);
  }
}, 10000);

L4 order book poller

Poll the L4 order book (individual orders with wallet addresses) via REST.
const API_KEY = "pn_live_YOUR_KEY";
const BASE = "https://hyper.polynode.dev";

async function getL4Book(coin: string) {
  const res = await fetch(`${BASE}/v2/l4book?coin=${coin}`, {
    headers: { "x-api-key": API_KEY }
  });
  const book = await res.json();

  const bids = book.levels.filter((l: any) => l.side === "B").sort((a: any, b: any) => parseFloat(b.px) - parseFloat(a.px));
  const asks = book.levels.filter((l: any) => l.side === "A").sort((a: any, b: any) => parseFloat(a.px) - parseFloat(b.px));

  console.log(`${coin} L4 Book — ${bids.length} bids | ${asks.length} asks`);
  console.log("\nTop 10 Bids:");
  for (const b of bids.slice(0, 10)) {
    console.log(`  ${b.px}  ${b.sz}  ${b.user.slice(0, 8)}...`);
  }
  console.log("\nTop 10 Asks:");
  for (const a of asks.slice(0, 10)) {
    console.log(`  ${a.px}  ${a.sz}  ${a.user.slice(0, 8)}...`);
  }
}

// Poll every 5 seconds
setInterval(() => getL4Book("BTC"), 5000);
getL4Book("BTC");

Rejection monitor

Track order rejections across all assets. Useful for identifying accounts under margin stress.
import WebSocket from "ws";

const ws = new WebSocket("wss://hyper.polynode.dev/ws?key=pn_live_YOUR_KEY");
const rejectionCounts: Map<string, { count: number; reasons: string[] }> = new Map();

ws.on("open", () => {
  ws.send(JSON.stringify({
    action: "subscribe",
    filters: { action_types: ["order"] }
  }));
});

ws.on("message", (raw) => {
  const event = JSON.parse(raw.toString());
  if (event.type !== "order_rejected") return;

  const wallet = event.user;
  const reason = event.data.status;

  if (!rejectionCounts.has(wallet)) {
    rejectionCounts.set(wallet, { count: 0, reasons: [] });
  }
  const entry = rejectionCounts.get(wallet)!;
  entry.count++;
  entry.reasons.push(reason);

  if (entry.count >= 5) {
    console.log(`HIGH REJECTION RATE: ${wallet}`);
    console.log(`  ${entry.count} rejections: ${[...new Set(entry.reasons)].join(", ")}`);
    rejectionCounts.delete(wallet); // Reset
  }
});

REST + stream combination

Use REST for initial state, stream for updates.
import WebSocket from "ws";

const API_KEY = "pn_live_YOUR_KEY";
const WALLET = "0xd071d6d6ea52f5aa34b79e47f908ee48c8215837";

// 1. Get current positions via REST
async function getPositions() {
  const res = await fetch(`https://hyper.polynode.dev/v2/user/state?address=${WALLET}`, {
    headers: { "x-api-key": API_KEY }
  });
  return await res.json();
}

// 2. Stream fills for real-time updates
function streamFills() {
  const ws = new WebSocket(`wss://hyper.polynode.dev/ws?key=${API_KEY}`);

  ws.on("open", () => {
    ws.send(JSON.stringify({
      action: "subscribe",
      filters: { addresses: [WALLET], action_types: ["fill"] }
    }));
  });

  ws.on("message", (raw) => {
    const event = JSON.parse(raw.toString());
    if (event.type === "fill") {
      console.log(`Position update: ${event.asset} ${event.data.dir} ${event.data.sz}@${event.data.px}`);
      // Update local state
    }
  });
}

// Start both
getPositions().then((positions) => {
  console.log("Initial positions:", positions);
  streamFills();
});

Pattern: Watch for liquidations

Stream every liquidation happening across HyperLiquid in real-time. Useful for liquidation bots, market stress monitors, and building a public liquidation feed.
import WebSocket from "ws";

const API_KEY = process.env.HYPERNODE_API_KEY;
const ws = new WebSocket(`wss://hyper.polynode.dev/ws?key=${API_KEY}`);

ws.on("open", () => {
  ws.send(JSON.stringify({
    action: "subscribe",
    filters: {
      action_types: ["fill"],
      liquidations_only: true
    }
  }));
});

ws.on("message", (raw) => {
  const event = JSON.parse(raw.toString());
  if (event.type !== "fill") return;

  const { coin, sz, px, closedPnl } = event.data;
  const { liquidatedUser, markPx, method } = event.data.liquidation;

  console.log(
    `LIQUIDATION: ${coin} sz=${sz} markPx=${markPx} ` +
    `user=${liquidatedUser} pnl=${closedPnl} method=${method}`
  );
});
Every liquidation generates at least 2 fill events — one for the liquidated user and one for the liquidator(s). Both carry the same data.liquidation object and share the same tid and hash so you can correlate them.

Pattern: Builder DEX attribution feed

If you operate a HIP-3 builder DEX, subscribe to fills attributed to your builder address to track your flow in real-time without polling userFills per wallet.
import WebSocket from "ws";

const API_KEY = process.env.HYPERNODE_API_KEY;
const BUILDER = "0x1924b8561eef20e70ede628a296175d358be80e5";
const ws = new WebSocket(`wss://hyper.polynode.dev/ws?key=${API_KEY}`);

ws.on("open", () => {
  ws.send(JSON.stringify({
    action: "subscribe",
    filters: {
      action_types: ["fill"],
      builders: [BUILDER]
    }
  }));
});

ws.on("message", (raw) => {
  const event = JSON.parse(raw.toString());
  if (event.type !== "fill") return;

  const { coin, sz, px, builderFee } = event.data;
  console.log(`Fill via our builder: ${coin} ${sz}@${px} fee=${builderFee}`);
});
You can pass multiple builder addresses if you operate several DEXes.

Pattern: All fills firehose for market analytics

Subscribe to every fill on every market. Useful for VWAP calculations, open interest tracking, and market-wide analytics.
const ws = new WebSocket(`wss://hyper.polynode.dev/ws?key=${API_KEY}`);

ws.on("open", () => {
  ws.send(JSON.stringify({
    action: "subscribe",
    filters: { action_types: ["fill"] }
  }));
});

const volumeByAsset: Record<string, number> = {};

ws.on("message", (raw) => {
  const event = JSON.parse(raw.toString());
  if (event.type !== "fill") return;

  const coin = event.data.coin;
  const notional = parseFloat(event.data.sz) * parseFloat(event.data.px);
  volumeByAsset[coin] = (volumeByAsset[coin] || 0) + notional;
});

// Log top 5 by volume every 10 seconds
setInterval(() => {
  const top = Object.entries(volumeByAsset)
    .sort(([, a], [, b]) => b - a)
    .slice(0, 5);
  console.log("Top 5 by volume:", top);
}, 10000);

Pattern: Batch portfolio query for multi-wallet platforms

Fetch portfolio performance for thousands of wallets in a single request. Designed for copy trading leaderboards, portfolio trackers, and multi-user dashboards.
async function batchPortfolios(wallets: string[]) {
  const res = await fetch("https://hyper.polynode.dev/v2/batch/portfolios", {
    method: "POST",
    headers: {
      "x-api-key": process.env.HYPERNODE_API_KEY!,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ users: wallets }),
  });
  return await res.json();
}

// Up to 10,000 wallets per call
const wallets = [
  "0xacd89cFCB82Ae1f843467D56b58796bb928C9E1A",
  "0x711d5ee8df7f0075f5393f7c9197248e961542b1",
  // ... up to 10000 more
];

const portfolios = await batchPortfolios(wallets);
for (const [address, data] of Object.entries(portfolios)) {
  if (data) {
    console.log(address, "portfolio:", data);
  }
}
Paired with /v2/batch/clearinghouse-states you can build a complete leaderboard snapshot of thousands of traders in well under a second.