BlockReq
返回全部文章

Rpc Tutorials

Robinhood 抢开盘怎么盯:eth_subscribe 看盘 + 打标签

用 BlockReq 公共 WSS eth_subscribe(newHeads + 窄 logs)盯 Robinhood Chain 抢开盘:盘口节奏、模式打标签、断线回补;Free 3M 请求配额可直接开跑。

BlockReq Engineering2026年9月5日9 分钟阅读

Robinhood 抢开盘怎么盯:eth_subscribe 看盘 + 打标签

抢开盘时,盘口节奏往往先体现在链上:区块心跳池子 logs 突发观察地址进出。这篇用 BlockReq 公共 RPC(Free 3M 请求配额)把「打狗盘面」做成可订阅的监听栈——eth_subscribe 推流 + 模式打标签,方便看板与告警接线。

用途Endpoint
公共 HTTPShttps://robinhood-mainnet-rpc.blockreq.com/v1/rpc/public
公共 WSSwss://robinhood-mainnet-rpc.blockreq.com/v1/rpc/public
私有 HTTPS / WSS把末尾 public 换成你的 {API_KEY}(Key 放本地配置)

链身份:EVM,chainId = 0x1237(十进制 4663)。文档:公开端点 · eth_subscribe · Supported Networks · 套餐与定价(Free 3M Requests / 30 days)。

若你要盯的是 Solana 新 mint(另一条链上的发射窗口),可交叉阅读:浏览器里监听 Solana 新币发射logsSubscribe)。本文默认 Robinhood Chain 的 EVM eth_subscribe

1. 抢开盘盯什么:区块心跳 / 池子 logs / 观察地址进出

开盘窗口里,三类信号最适合直接挂到订阅上:

盯什么链上对应盘面语感
区块心跳newHeads:高度、时间戳、gasUsed / baseFee出块稳不稳、gas 有没有突然拉高
池子 logs窄过滤的 logs:Transfer / Swap / 自定义 topic池子突然动了、同块插针般的事件簇
观察地址进出指定 address 的 logs,或 HTTP eth_getBalance / eth_getTransactionCount金库、做市账户、你自己的 watch list

把每条推送落成结构化字段(blockNumbertxHashaddresstopics),后面打标签、告警、对账都好接。

2. 轮询 vs WSS

HTTPS 轮询WSS eth_subscribe
典型调用eth_blockNumbereth_getLogseth_getTransactionReceiptnewHeadslogs
延迟取决于轮询间隔 + 限流推送,更贴盘口节奏
空窗你自己控 fromBlock / toBlock断线后用 HTTP 回补(见第 5 节)
限流压力高频轮询容易打满 public IP / Free 配额订阅本身轻;重连风暴仍会撞限流
适用低频看板、对账回放、补洞抢开盘几分钟的实时心跳与事件流

怎么选(短):

  • 只确认「还在出块」→ 30–60s 轮询 eth_blockNumber 往往够用。
  • 开盘窗口要盯事件流 → WSS 订 newHeads + 窄过滤 logs
  • 生产正确性 → 订阅负责低延迟,HTTP eth_getLogs 负责断线回补

3. BlockReq public(Free 3M):eth_subscribe newHeads + 窄 logs;keyed;StackBlitz

EVM 侧方法名是 eth_subscribe(WSS only)。Solana 的 logsSubscribe 是另一套协议,别混在同一个 client 里硬套。

公共 endpoint 适合打开页面盯几分钟;账号侧 Free 套餐带 3M Requests / 30 days,长期 7×24 心跳可换成 keyed(Dashboard 创建 Key,路由确认 WSS 已开)。

3.1 订新头 newHeads

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "eth_subscribe",
  "params": ["newHeads"]
}

成功后先收到 subscription id,随后推送 eth_subscriptionparams.result 里是区块头字段(numbertimestamphashgasUsed 等)。

3.2 订日志 logs(窄过滤)

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "eth_subscribe",
  "params": [
    "logs",
    {
      "address": "0xYourPoolOrTokenAddress",
      "topics": [
        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
      ]
    }
  ]
}

说明:

  • topics[0] 示例是 ERC-20 Transfer(address,address,uint256) 的事件签名哈希;换成你真正关心的 Swap / Mint 事件即可。
  • 务必加 address(或尽量窄的 topics)。裸订全链 logs 噪音大,也更容易撞 public / Free 配额。
  • 0xYourPoolOrTokenAddress 换成你自己要观察的合约;下方 demo 默认只订 newHeads,可选勾选 Transfer logs。

3.3 public vs keyed

公共 endpoint私有(keyed)
URL…/v1/rpc/public…/v1/rpc/{API_KEY}
限速Best-effort,按 IP按套餐 RU / req/s
配额入口Free 3M Requests / 30 days(注册后)Starter / Growth / …
适合学习、短时抢开盘盯盘 demo长期看板、告警、生产订阅
KeyDashboard 创建;放本地环境变量

更多:公开端点 · 套餐与定价

3.4 浏览器可跑:单文件 HTML(默认 public)

把下面存成 robinhood-open-watch.html,用本地静态服务器打开(部分浏览器对 file:// 的 WebSocket 更挑,推荐 npx serve)。只连 public,无 Key。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <title>Robinhood Chain 抢开盘盯盘(newHeads + 打标签)</title>
  <style>
    body { font-family: ui-sans-serif, system-ui, sans-serif; margin: 1.5rem; max-width: 52rem; }
    button { margin-right: 0.5rem; }
    #status { margin: 0.75rem 0; color: #334155; }
    pre { background: #0f172a; color: #e2e8f0; padding: 1rem; min-height: 16rem;
          overflow: auto; font-size: 12px; border-radius: 8px; }
    code { background: #f1f5f9; padding: 0.1rem 0.35rem; border-radius: 4px; }
    .tag { display: inline-block; background: #fef3c7; color: #92400e;
           padding: 0.05rem 0.4rem; border-radius: 4px; font-size: 11px; margin-left: 0.35rem; }
  </style>
</head>
<body>
  <h1>Robinhood Chain · 抢开盘盯盘</h1>
  <p>公共 WSS · <code>eth_subscribe → newHeads</code> · 可选 Transfer logs · 模式打标签</p>
  <p>
    WSS:<code>wss://robinhood-mainnet-rpc.blockreq.com/v1/rpc/public</code><br />
    Free 3M 配额够跑短时 demo;长期看板换 keyed。
  </p>
  <label>
    <input type="checkbox" id="alsoLogs" />
    同时订 Transfer logs(需填写 address)
  </label>
  <p>
    <input id="watchAddress" placeholder="0x… 合约地址(可选)" size="48" />
  </p>
  <button id="start" type="button">Start</button>
  <button id="stop" type="button" disabled>Stop</button>
  <button id="clear" type="button">Clear</button>
  <div id="status">Idle</div>
  <pre id="out"></pre>
  <script>
    const WSS = "wss://robinhood-mainnet-rpc.blockreq.com/v1/rpc/public";
    const TRANSFER =
      "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
    const out = document.getElementById("out");
    const status = document.getElementById("status");
    let ws = null;
    let nextId = 1;
    let backoffMs = 1000;
    let wantRun = false;
    const seen = new Set();
    const headsByBlock = new Map();
    const logsByBlock = new Map();
    let lastGasUsed = null;

    function log(line) {
      const t = new Date().toISOString().slice(11, 19);
      out.textContent = `[${t}] ${line}\n` + out.textContent;
    }
    function setStatus(s) { status.textContent = s; }

    function send(method, params) {
      const id = nextId++;
      ws.send(JSON.stringify({ jsonrpc: "2.0", id, method, params }));
      return id;
    }

    function tagHead(r) {
      const tags = [];
      const gasUsed = r.gasUsed ? parseInt(r.gasUsed, 16) : null;
      if (gasUsed != null && lastGasUsed != null && gasUsed > lastGasUsed * 1.5) {
        tags.push("gas尖峰");
      }
      if (gasUsed != null) lastGasUsed = gasUsed;
      const n = parseInt(r.number, 16);
      const logCount = (logsByBlock.get(n) || []).length;
      if (logCount >= 3) tags.push("同块密集logs");
      return tags;
    }

    function tagLog(r) {
      const tags = [];
      const n = r.blockNumber ? parseInt(r.blockNumber, 16) : null;
      if (n != null) {
        const arr = logsByBlock.get(n) || [];
        arr.push(r);
        logsByBlock.set(n, arr);
        if (arr.length >= 3) tags.push("同块插针");
        const sameTo = arr.filter((x) => (x.address || "").toLowerCase() === (r.address || "").toLowerCase());
        if (sameTo.length >= 2) tags.push("同一to突发");
      }
      tags.push("池子Transfer");
      return tags;
    }

    function subscribeAll() {
      send("eth_subscribe", ["newHeads"]);
      log("sent eth_subscribe newHeads");
      const addr = document.getElementById("watchAddress").value.trim();
      if (document.getElementById("alsoLogs").checked) {
        if (!/^0x[a-fA-F0-9]{40}$/.test(addr)) {
          log("skip logs: 请填写合法 0x 地址");
          return;
        }
        send("eth_subscribe", ["logs", { address: addr, topics: [TRANSFER] }]);
        log("sent eth_subscribe logs Transfer @ " + addr);
      }
    }

    function connect() {
      if (!wantRun) return;
      setStatus("connecting…");
      ws = new WebSocket(WSS);
      ws.onopen = () => {
        backoffMs = 1000;
        setStatus("connected · subscribed");
        subscribeAll();
      };
      ws.onmessage = (ev) => {
        let msg;
        try { msg = JSON.parse(ev.data); } catch { return; }
        if (msg.id && msg.result && typeof msg.result === "string") {
          log("subscription id=" + msg.result);
          return;
        }
        if (msg.method !== "eth_subscription") return;
        const r = msg.params && msg.params.result;
        if (!r) return;
        if (r.number) {
          const n = parseInt(r.number, 16);
          const key = "b:" + r.hash;
          if (seen.has(key)) return;
          seen.add(key);
          headsByBlock.set(n, r);
          const tags = tagHead(r);
          const tagStr = tags.length ? " [" + tags.join(",") + "]" : "";
          log(`head #${n} hash=${r.hash.slice(0, 12)}… ts=${parseInt(r.timestamp, 16)}${tagStr}`);
          return;
        }
        if (r.transactionHash) {
          const key = "l:" + r.transactionHash + ":" + r.logIndex;
          if (seen.has(key)) return;
          seen.add(key);
          const tags = tagLog(r);
          const tagStr = tags.length ? " [" + tags.join(",") + "]" : "";
          log(`log tx=${r.transactionHash.slice(0, 12)}… addr=${r.address}${tagStr}`);
        }
      };
      ws.onclose = () => {
        setStatus("closed");
        if (!wantRun) return;
        setStatus(`reconnect in ${backoffMs}ms`);
        setTimeout(connect, backoffMs);
        backoffMs = Math.min(backoffMs * 2, 30000);
      };
      ws.onerror = () => { try { ws.close(); } catch {} };
    }

    document.getElementById("start").onclick = () => {
      wantRun = true;
      document.getElementById("start").disabled = true;
      document.getElementById("stop").disabled = false;
      connect();
    };
    document.getElementById("stop").onclick = () => {
      wantRun = false;
      document.getElementById("start").disabled = false;
      document.getElementById("stop").disabled = true;
      if (ws) try { ws.close(); } catch {}
      setStatus("stopped");
    };
    document.getElementById("clear").onclick = () => {
      out.textContent = "";
      seen.clear();
      logsByBlock.clear();
      lastGasUsed = null;
    };
  </script>
</body>
</html>

在线示例已落到 blockreq/blog-demosexamples/robinhood-open-watchmain 分支)。换 keyed:只改 WSS 常量为 wss://robinhood-mainnet-rpc.blockreq.com/v1/rpc/{API_KEY},Key 放本地环境变量。

4. 识别相关模式(打标签)

订阅推过来的是原始头 / log;「打狗盘面」的可读性来自 本地打标签。常见标签与判定启发式:

标签信号怎么抓实现提示
同块密集 tx / logs同一 blockNumber 短时堆积多条 log 或 receipt按块号桶计数,阈值 ≥ N 打标
同一 to多笔 tx / log 指向同一合约 address(或 receipt to分组 address.toLowerCase()
gas / priority 尖峰gasUsedbaseFeePerGas、或 receipt 的 effectiveGasPrice 相对滑动窗口陡升与上一头 / 近 N 块均值比较
池子 Transfer / Swap 突发logs 在窗口内速率突增按 topic0 + address 做滑动计数

标签是 解释层:把 JSON 推送变成看板色块、告警文案、Webhook payload 字段。阈值按你盯的池子自己调;demo 里用了简易阈值方便上手。

进阶可以再订一层:对打了「同块插针」的块号,补一次 eth_getBlockByNumber(…, true) 看同块 tx 列表,或对相关 hash 拉 eth_getTransactionReceipt 核对 to / gas。

5. 断线重连、限流、eth_getLogs 回补

  1. 断线必发生 — 浏览器休眠、网络抖动、公共节点踢空闲连接都很常见。Demo 用指数退避重连并重新 subscribe
  2. 空窗要回补 — 记下最后处理的 blockNumber,重连后用 HTTPS:
{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "eth_getLogs",
  "params": [{
    "fromBlock": "0xFROM",
    "toBlock": "0xTO",
    "address": "0xYourPoolOrTokenAddress",
    "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
  }]
}

POST 到 https://robinhood-mainnet-rpc.blockreq.com/v1/rpc/public。窗口按块分段,避免一次拉太大撞 public / Free 配额。

  1. 限流表现 — HTTP 429 / 空响应 / WSS 秒断。对策:收窄订阅、拉长回补间隔、开盘高峰换 keyed、同一 IP 少开「全链 logs」。
  2. 去重 — 用 blockHash / txHash+logIndex Set,避免重放刷屏。
  3. pendingnewPendingTransactions 噪音大,且路由未必稳定开启;抢开盘优先 newHeads + 窄 logs

6. 小结 + public / Free 3M

  1. 抢开盘盯盘 = Robinhood Chain(0x1237)上的实时窗口:区块心跳、池子 logs、观察地址进出。
  2. 低频用心跳轮询;开盘事件流用 newHeads + 窄过滤 logs;本地打标签(同块密集、同一 to、gas 尖峰、池子突发)。
  3. 断线用 eth_getLogs 回补;去重 + 分段窗口保吞吐。
  4. 公共 WSS / HTTPS 可直接跑 demo;账号 Free 3M 请求配额够短时盯盘;长期看板换 keyed。

下一步通常是:固定观察地址列表 → 把标签接到 Webhook / 看板 → 把回补 checkpoint 落盘。公共入口与配额说明见 公开端点套餐与定价