Robinhood Chain 测试网入门:免 key 读数据、WebSocket 订阅、再切主网
面向 Robinhood Chain 测试网的三步 viem 模板:免 key 公开 JSON-RPC 读数据、程序化钱包开户并订阅 WebSocket logs,再把这把 key 用到主网。
BlockVectra 为 Robinhood Chain 测试网(链 ID 46630)提供 JSON-RPC 与 WebSocket 访问,同一把 API key 也能用于 Robinhood Chain 主网。本指南按 Robinhood Chain 测试网入门模板 的三步走:免 key 读测试网、程序化开户并订阅 logs、再把同一把 key 切到主网。端点参数、方法策略与代币化股票数据见 Robinhood Chain 指南。
运行模板
git clone https://github.com/blockvectra/robinhood-testnet-starter
cd robinhood-testnet-starter
npm install
npm run typecheck需要 Node.js 18 及以上。三个脚本分别是 npm run step1、npm run step2 和 npm run step3。可选变量(BLOCKVECTRA_API_KEY、WALLET_PRIVATE_KEY)从 shell 读取(模板不自动加载 .env)。
第 1 步:免 key 读测试网
公开端点 https://api.blockvectra.com/v1/robinhood_testnet/public 提供钱包类 JSON-RPC 方法,无需账户与 API key。直接查询 EIP-155 链 ID:
curl -s "https://api.blockvectra.com/v1/robinhood_testnet/public" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"eth_chainId","params":[]}'返回为 {"jsonrpc":"2.0","id":1,"result":"0xb626"};0xb626 是十六进制链 ID,即十进制的 46630。
模板从 GET /v1/chains 读取链信息并据此构造 viem 客户端,链名与链 ID 都不写死。src/shared.ts:
import { createPublicClient, defineChain, http, webSocket } from "viem";
export const API_BASE = "https://api.blockvectra.com/v1";
export interface ChainEntry {
chain: string;
name: string;
chain_id: number;
data: boolean;
ws: boolean;
subscriptions: string[];
public?: { url: string };
}
export async function getChain(slug: string): Promise<ChainEntry> {
const res = await fetch(`${API_BASE}/chains`);
if (!res.ok) throw new Error(`GET /v1/chains failed: ${res.status}`);
const { chains } = (await res.json()) as { chains: ChainEntry[] };
const entry = chains.find((c) => c.chain === slug);
if (!entry) throw new Error(`${slug} not found in /v1/chains`);
return entry;
}
export function toViemChain(entry: ChainEntry, rpcUrl: string) {
return defineChain({
id: entry.chain_id,
name: entry.name,
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
rpcUrls: { default: { http: [rpcUrl] } },
});
}
export function httpClient(entry: ChainEntry, url: string, apiKey?: string) {
return createPublicClient({
chain: toViemChain(entry, url),
transport: http(url, apiKey ? { fetchOptions: { headers: { "x-api-key": apiKey } } } : undefined),
});
}
export function wsClient(entry: ChainEntry, apiKey: string) {
const base = "wss://api.blockvectra.com/v1";
const url = `${base}/${entry.chain}/${apiKey}`;
return createPublicClient({ chain: toViemChain(entry, url), transport: webSocket(url) });
}在此基础上,src/step1-public-read.ts 读取链 ID、最新区块与某地址余额:
import { formatEther, type Address } from "viem";
import { API_BASE, getChain, httpClient } from "./shared.js";
const SLUG = "robinhood_testnet";
// 任意地址均可;也可用第一个命令行参数覆盖。
const address = (process.argv[2] ?? "0x0000000000000000000000000000000000000000") as Address;
const chain = await getChain(SLUG);
const client = httpClient(chain, `${API_BASE}/${SLUG}/public`);
const [chainId, blockNumber, balance] = await Promise.all([
client.getChainId(),
client.getBlockNumber(),
client.getBalance({ address }),
]);
console.log(`chain : ${chain.name} (${SLUG})`);
console.log(`eth_chainId : ${chainId} (0x${chainId.toString(16)})`);
console.log(`block : ${blockNumber}`);
console.log(`balance : ${formatEther(balance)} ETH (${address})`);第 2 步:程序化开户并订阅 logs
src/step2-key-and-logs.ts 在 https://console-api.blockvectra.com/v1 用钱包签名(SIWE,EIP-191)登录。首次登录会自动开户,随后客户端创建 API key。新账户注册即得 3,000 万 CU,无需信用卡。
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
import type { Hex } from "viem";
const CONSOLE_API = "https://console-api.blockvectra.com/v1";
const REF = "gh-robinhood-testnet-starter";
async function post<T>(path: string, body: unknown, token?: string): Promise<T> {
const res = await fetch(`${CONSOLE_API}${path}`, {
method: "POST",
headers: { "Content-Type": "application/json", ...(token ? { Authorization: `Bearer ${token}` } : {}) },
body: JSON.stringify(body),
});
if (!res.ok) throw new Error(`${path} failed: ${res.status} ${await res.text()}`);
return (await res.json()) as T;
}
async function provisionKey(): Promise<string> {
const pk = (process.env.WALLET_PRIVATE_KEY as Hex | undefined) ?? generatePrivateKey();
const account = privateKeyToAccount(pk);
console.log(`wallet: ${account.address}`);
// SIWE:先取挑战消息,原样签名(EIP-191)后登录;首次登录会创建账户。
const { message } = await post<{ message: string }>("/auth/siwe/challenge", {
address: account.address,
purpose: "login",
});
const signature = await account.signMessage({ message });
const login = await post<{ session: { token: string }; account_created: boolean }>("/auth/siwe/login", {
message,
signature,
ref: REF,
});
console.log(`account_created: ${login.account_created}`);
const created = await post<{ api_key: string }>("/keys", { label: "robinhood-testnet-starter" }, login.session.token);
return created.api_key;
}
const apiKey = process.env.BLOCKVECTRA_API_KEY?.trim() || (await provisionKey());已有 key 时,export BLOCKVECTRA_API_KEY=... 会跳过开户。不设置 WALLET_PRIVATE_KEY 时,脚本会在内存里生成一次性钱包,运行结束即丢弃;想再次登录同一账户,请从本地环境变量提供 WALLET_PRIVATE_KEY。
WebSocket URL 把 key 放在路径中:wss://api.blockvectra.com/v1/robinhood_testnet/{api_key}。模板在订阅前会检查 GET /v1/chains 返回的 ws 标记与 subscriptions 列表:
const chain = await getChain("robinhood_testnet");
if (!chain.ws || !chain.subscriptions.includes("logs")) {
throw new Error(`robinhood_testnet does not offer a logs subscription in /v1/chains`);
}
console.log(`subscriptions (from /v1/chains): ${chain.subscriptions.join(", ")}`);
const client = wsClient(chain, apiKey);
const stop = client.watchEvent({
address: "0x1234567890123456789012345678901234567890",
onLogs: (logs) => {
for (const l of logs) console.log(`block ${l.blockNumber} ${l.address} topic0=${l.topics[0]}`);
},
onError: (e) => console.error("subscription error:", e.message),
});每个 logs 订阅过滤器必须包含 address 或 topic0;请把示例地址替换成你要监听的合约。过滤规则、关闭码与重连策略见 WebSocket 订阅指南。
第 3 步:把同一把 key 切到主网
切到主网就是把链名改成 robinhood_mainnet,key 不变。src/step3-mainnet.ts 读取主网区块高度,并调用 Robinhood 股票代币的 Data API:
const apiKey = process.env.BLOCKVECTRA_API_KEY?.trim();
if (!apiKey) throw new Error("Set BLOCKVECTRA_API_KEY (run step 2 first, or create a key in the console).");
const mainnet = await getChain("robinhood_mainnet");
// 带 key 的 JSON-RPC:链名换成主网即可。
const client = httpClient(mainnet, `${API_BASE}/${mainnet.chain}`, apiKey);
console.log(`mainnet block: ${await client.getBlockNumber()}`);
// Data API:Robinhood 股票代币活跃榜单(见 Robinhood Chain 指南)。
const res = await fetch(`${API_BASE}/data/${mainnet.chain}/stocks`, { headers: { "x-api-key": apiKey } });
if (!res.ok) throw new Error(`Data API failed: ${res.status} ${await res.text()}`);
console.log(JSON.stringify(await res.json(), null, 2).slice(0, 2000));同一个榜单端点的 curl:
curl -s -H "x-api-key: $BLOCKVECTRA_API_KEY" \
"https://api.blockvectra.com/v1/data/robinhood_mainnet/stocks"主网提供 Data API,测试网没有:GET /v1/chains 中 robinhood_testnet 的 data 为 false,robinhood_mainnet 的 data 为 true。请求参数与响应外层结构见 代币化股票指南。
从接口读取限额与能力
用 GET /v1/chains 读取实时的 jsonrpc、data、ws、subscriptions 标记、开放的 JSON-RPC 方法、日志区块跨度、状态保留窗口以及各链的公开端点;用 GET /v1/plans 读取当前价格与方案限额。
curl -s "https://api.blockvectra.com/v1/chains"下一步
最后更新: