HoodFi 与 Robinhood Chain:读取 coin type 2147488311 名称记录
用 viem 读取 HoodFi 名称的 Robinhood 地址,选择 coin type 2147488311,并在同一规范区块哈希比较 coin type 60。
选择 Robinhood 记录
读取 Robinhood Chain(4663)上的 HoodFi 名称时,用 addr(bytes32,uint256) 读取 coin type 2147488311,再在同一区块哈希比较 coin type 60。选择 Robinhood 记录;如果为空、零地址或不是恰好 20 bytes,立即停止,不回退到 coin type 60 或默认记录。两个地址不同时,使用前请用户确认 Robinhood 目标地址。
这遵循维护者的记录选择策略。ENSIP-11 用 0x80000000 | chainId 推导 EVM coin type:4663 对应 2147488311。单参数的 addr(bytes32) 读取 coin type 60,不能用它选择 Robinhood 记录。
本配方调用固定的 HoodFi 注册合约 0xf2bABA012244bdD7445129597350054E1B3aEe5C,以 namehash(NAME) 传入节点;这不是通用 ENS 解析器发现流程。
运行读取脚本
准备 Node.js 20+ 和 viem 2.x(在自己的项目执行 npm install viem)。从控制台获取 key,或使用程序化注册指南;初次配置见快速开始。在本机将 BLOCKVECTRA_API_KEY 设为真实 key,把下面的 JavaScript 保存为 hoodfi-name-records.mjs,执行 node hoodfi-name-records.mjs。可选设置 NAME,默认值为 mike.hoodfi.eth。不要把 key 写进源码、URL 或日志。
脚本请求 https://api.blockvectra.com/v1/robinhood_mainnet,key 只放在 x-api-key 请求头。最多串行发出四次请求,间隔至少 1.1 秒,每次超时 15 秒,不跟随重定向、不重试:
- 核对
eth_chainId为0x1237(4663)。 - 只读取一次
latest,保留区块哈希。 - 用
{blockHash, requireCanonical: true}读取 coin type 2147488311;Robinhood 地址无效即停止。 - 用完全相同的区块选择器读取 coin type 60,仅用于比较。空值或零地址表示没有比较记录,其他畸形字节使脚本停止。
import { decodeFunctionResult, encodeFunctionData, namehash, parseAbi } from "viem";
const ENDPOINT = "https://api.blockvectra.com/v1/robinhood_mainnet";
const REGISTRY = "0xf2bABA012244bdD7445129597350054E1B3aEe5C";
const NAME = process.env.NAME ?? "mike.hoodfi.eth";
const RH_COIN_TYPE = 2147488311n; // ENSIP-11: 0x80000000 | 4663
const GAP_MS = 1_100;
const TIMEOUT_MS = 15_000;
const apiKey = process.env.BLOCKVECTRA_API_KEY;
if (!apiKey || /\s|replace|placeholder|your[_-]?(api[_-]?)?key|[<>]/i.test(apiKey)) {
throw new Error("Set a real BLOCKVECTRA_API_KEY in your local environment");
}
const abi = parseAbi(["function addr(bytes32 node, uint256 coinType) view returns (bytes)"]);
const node = namehash(NAME); // A node for this fixed HoodFi registry, not general ENS resolution.
// Raw JSON-RPC preserves the EIP-1898 object without client-side block tag rewriting.
let sent = 0;
let lastSentAt;
async function rpc(method, params) {
if (sent >= 4) throw new Error("Four-request diagnostic budget exceeded");
if (lastSentAt !== undefined) {
const wait = lastSentAt + GAP_MS - Date.now();
if (wait > 0) await new Promise((resolve) => setTimeout(resolve, wait));
}
lastSentAt = Date.now();
const id = ++sent;
let response;
try {
response = await fetch(ENDPOINT, {
method: "POST",
headers: {
"content-type": "application/json",
"x-api-key": apiKey,
"user-agent": "hoodfi-name-records-diagnostic BlockVectraQA/1",
},
body: JSON.stringify({ jsonrpc: "2.0", id, method, params }),
redirect: "error",
signal: AbortSignal.timeout(TIMEOUT_MS),
});
} catch {
throw new Error(`${method}: transport failure (including timeout or redirect)`);
}
if (response.status !== 200) throw new Error(`${method}: HTTP ${response.status}`);
let body;
try {
body = await response.json();
} catch {
throw new Error(`${method}: invalid JSON`);
}
if (!body || body.jsonrpc !== "2.0" || body.id !== id) {
throw new Error(`${method}: invalid JSON-RPC response id or version`);
}
if (Object.hasOwn(body, "error")) {
// Do not echo server messages or arbitrary response data: they can contain secrets.
const code = Number.isSafeInteger(body.error?.code) ? body.error.code : "unknown";
const reason = body.error?.data?.reason === "state_window" ? " state_window" : "";
throw new Error(`${method}: JSON-RPC error ${code}${reason}`);
}
if (!Object.hasOwn(body, "result")) throw new Error(`${method}: missing result`);
return body.result;
}
const chainId = await rpc("eth_chainId", []);
if (typeof chainId !== "string" || !/^0x(?:0|[1-9a-f][0-9a-f]*)$/i.test(chainId)
|| BigInt(chainId) !== 4663n) throw new Error("Expected chain 4663 (0x1237)");
const head = await rpc("eth_getBlockByNumber", ["latest", false]);
const blockHash = head?.hash;
if (typeof blockHash !== "string" || !/^0x[0-9a-f]{64}$/i.test(blockHash)) {
throw new Error("Invalid block hash");
}
const at = { blockHash, requireCanonical: true };
async function readAddress(coinType, label, optional) {
const result = await rpc("eth_call", [{
to: REGISTRY,
data: encodeFunctionData({ abi, functionName: "addr", args: [node, coinType] }),
}, at]);
let bytes;
try {
if (typeof result !== "string" || !/^0x(?:[0-9a-f]{2})*$/i.test(result)) throw new Error();
bytes = decodeFunctionResult({ abi, functionName: "addr", data: result });
} catch {
throw new Error(`${label}: invalid ABI result`);
}
if (bytes === "0x" || /^0x0{40}$/i.test(bytes)) {
if (optional) return null;
throw new Error(`${label}: missing or zero address`);
}
if (!/^0x[0-9a-f]{40}$/i.test(bytes)) throw new Error(`${label}: expected exactly 20 bytes`);
return bytes.toLowerCase();
}
// Request three must succeed before coin type 60 is even read.
const robinhoodAddress = await readAddress(RH_COIN_TYPE, "Robinhood record", false);
const coin60 = await readAddress(60n, "Coin type 60 record", true);
const warnings = [];
if (coin60 !== null && coin60 !== robinhoodAddress) {
warnings.push("Records differ: confirm the Robinhood destination before use; coin type 60 is comparison only.");
}
console.log(JSON.stringify({ blockHash, robinhoodAddress, coin60, warnings }, null, 2));输出仅含 blockHash、robinhoodAddress、coin60(或 null)与 warnings。使用地址前先检查警告。脚本不生成、签名或发送交易,User-Agent 标记本次为诊断运行。
HTTP 非 200、JSON-RPC 错误、响应 id 非法、缺少 result、哈希非法或 ABI 解码异常都会停止脚本,不输出部分结果。哈希读取遭拒时,不得换成 latest 或区块号继续读取。需要新快照时,从核对链 ID 开始重新运行。
访问方式与快照语义
EIP-1898 用哈希指定区块,requireCanonical: true 使非规范区块被拒绝。在同一哈希接受的两次读取,也不保证该区块将来不会重组。
本配方使用带 key 的入口。免 key 的 /v1/robinhood_mainnet/public 路由此前对这类固定哈希的 eth_call 返回 -32011 / state_window;仅能读取链头,不代表支持名称记录读取。当前能力与额度请查 GET /v1/chains 和定价页。
mike.hoodfi.eth 的一份两条记录匹配的快照见公开读取说明。使用地址前,请自行读取当前记录。
最后更新:
Robinhood Chain 主网
Robinhood Chain 主网链 ID 4663,免 key RPC 地址 https://api.blockvectra.com/v1/robinhood_mainnet/public;测试网链 ID 46630。用 viem 或 ethers 连接,用 Foundry 或 Hardhat 部署,订阅 WebSocket 日志并查询股票代币活动。
Robinhood Chain 测试网入门
Robinhood Chain 测试网 RPC 地址 https://api.blockvectra.com/v1/robinhood_testnet/public,链 ID 46630:免 key 读取、带 key 的 WebSocket 订阅、水龙头领取,以及把同一把 key 切到主网。