# HoodFi on Robinhood Chain: read coin type 2147488311 name records

> Original page: https://docs.blockvectra.com/en/guides/hoodfi-name-records/

## Choose the Robinhood record

For a HoodFi name on Robinhood Chain (4663), read `addr(bytes32,uint256)` with **coin type 2147488311**, then compare coin type 60 at the **same block hash**. Select the Robinhood record. If it is empty, zero or not exactly 20 bytes, stop; do not fall back to coin type 60 or a default record. If the two addresses differ, ask the user to confirm the Robinhood destination before using it.

This follows the [maintainer's record-selection policy](https://github.com/blockhash-xyz/mancer-sdk/issues/2#issuecomment-6622496038). [ENSIP-11](https://docs.ens.domains/ensip/11/) derives an EVM coin type as `0x80000000 | chainId`: for 4663, that is 2147488311. The one-argument `addr(bytes32)` reads coin type 60; it does not select a Robinhood record.

The recipe calls the fixed HoodFi registry `0xf2bABA012244bdD7445129597350054E1B3aEe5C`. `namehash(NAME)` supplies its node; this is not a general ENS resolver-discovery flow.

## Run the read

Use Node.js 20+ and viem 2.x (`npm install viem` in your own project). Get a key from the [console](https://console.blockvectra.com/login/?next=%2Fkeys%2F), or follow [programmatic sign-up](https://docs.blockvectra.com/en/guides/programmatic-signup/); see [Quickstart](https://docs.blockvectra.com/en/quickstart/) for initial setup. Set `BLOCKVECTRA_API_KEY` locally to your real key, then save the JavaScript below as `hoodfi-name-records.mjs` and run `node hoodfi-name-records.mjs`. Optionally set `NAME`; its default is `mike.hoodfi.eth`. Keep the key out of source files, URLs and logs.

The script uses `https://api.blockvectra.com/v1/robinhood_mainnet` with the key only in `x-api-key`. It makes at most four serial requests, at least 1.1 seconds apart, with a 15 second timeout per request, no redirects and no retries:

1. Verify `eth_chainId` is `0x1237` (4663).
2. Read `latest` once and retain the block hash.
3. Read coin type 2147488311 with `{blockHash, requireCanonical: true}`; reject an invalid Robinhood address immediately.
4. Read coin type 60 with exactly the same selector, for comparison only. Empty or zero means no comparison record; other malformed bytes stop the run.

```js
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));
```

Output contains only `blockHash`, `robinhoodAddress`, `coin60` (or `null`) and `warnings`. Read warnings before using the address. The script neither creates nor signs nor sends transactions. Its User-Agent marks a diagnostic run.

HTTP responses other than 200, JSON-RPC errors, invalid response ids, missing results, invalid hashes and ABI decoding failures stop the script without partial output. A rejected hash read must not be replaced with `latest` or a block number. To take a new snapshot, start a new run from the chain check.

## Access and snapshot semantics

[EIP-1898](https://eips.ethereum.org/EIPS/eip-1898) identifies the block by hash and makes `requireCanonical: true` reject a noncanonical block. Two reads accepted at the same hash do not guarantee that block cannot be reorganized later.

Use the keyed endpoint for this recipe. The keyless `/v1/robinhood_mainnet/public` route previously rejected this kind of hash-pinned `eth_call` with `-32011` / `state_window`; a head read alone does not establish support for the record reads. For current capabilities and allowances, check [GET /v1/chains](https://api.blockvectra.com/v1/chains) and [pricing](https://blockvectra.com/en/pricing/).

For a concrete matching-record snapshot of `mike.hoodfi.eth`, see the [public read explanation](https://github.com/blockhash-xyz/mancer-sdk/issues/2#issuecomment-6625982298). Read the current records yourself before using an address.
