# Robinhood Chain テストネット RPC スターター：キー不要の読み取り、WebSocket ログ、メインネット移行

> Source: https://docs.blockvectra.com/ja/guides/robinhood-testnet-starter/

BlockVectra は、Robinhood Chain テストネット（チェーン ID 46630）を JSON-RPC および WebSocket 経由で提供しており、同じ API key は Robinhood Chain メインネットでも利用できます。本ガイドでは、[Robinhood Chain テストネットスターター](https://github.com/blockvectra/robinhood-testnet-starter?ref=docs-testnet-starter) テンプレートに従い、3 つのステップ（キーなしでのテストネットの読み取り、プログラムによるアカウント作成とログのストリーミング、同じキーでのメインネットへの移行）を進めます。エンドポイントのパラメーター、メソッドポリシー、トークン化株式データについては、[Robinhood Chain ガイド](https://docs.blockvectra.com/en/guides/robinhood-chain/)を参照してください。

## テンプレートを実行する

```bash
git clone https://github.com/blockvectra/robinhood-testnet-starter
cd robinhood-testnet-starter
npm install
npm run typecheck
```

Node.js 18 以降が必要です。3 つのスクリプトは `npm run step1`、`npm run step2`、`npm run step3` です。任意の環境変数（`BLOCKVECTRA_API_KEY`、`WALLET_PRIVATE_KEY`）はシェルから読み取られます（`.env` は自動的には読み込まれません）。

## ステップ 1：キーなしでテストネットを読み取る

公開エンドポイント `https://api.blockvectra.com/v1/robinhood_testnet/public` では、アカウントも API key もなしでウォレット向け JSON-RPC メソッドが提供されます。EIP-155 チェーン ID を直接照会します：

```bash
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` は 16 進数のチェーン ID であり、10 進数では 46630 に相当します。

このテンプレートは `GET /v1/chains` からチェーンのエントリを読み取り、それをもとに viem クライアントを構築するため、チェーン名やチェーン ID はハードコードされていません。`src/shared.ts` より：

```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、最新ブロック、およびアドレスの残高を読み取ります：

```ts
import { formatEther, type Address } from "viem";
import { API_BASE, getChain, httpClient } from "./shared.js";

const SLUG = "robinhood_testnet";
// Any address works; override with the first CLI argument.
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：プログラムによってアカウントを作成しログを購読する

`src/step2-key-and-logs.ts` は、`https://console-api.blockvectra.com/v1` でウォレット署名（SIWE、EIP-191）を用いてログインします。初回ログイン時にアカウントが作成され、クライアントは API key を作成します。新規アカウントは登録時に 30,000,000 CU を受け取れます。クレジットカードは不要です。

```ts
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: ask for a challenge, sign it verbatim (EIP-191), log in. A first sign-in creates the account.
  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());
```

すでにキーをお持ちの場合は、`export BLOCKVECTRA_API_KEY=...` を設定すると登録処理がスキップされます。`WALLET_PRIVATE_KEY` が指定されていない場合、スクリプトはメモリ上で使い捨てのウォレットを生成し、実行終了時に破棄します。同じアカウントに再度ログインしたい場合は、ローカル環境から `WALLET_PRIVATE_KEY` を設定してください。

WebSocket URL ではパスにキーを含めます：`wss://api.blockvectra.com/v1/robinhood_testnet/{api_key}`。テンプレートは購読前に、`GET /v1/chains` から最新の `ws` フラグと `subscriptions` リストを確認します：

```ts
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 購読ガイド](https://docs.blockvectra.com/en/guides/websocket-subscriptions/)を参照してください。

## ステップ 3：同じキーをメインネットに切り替える

メインネットへの移行は、スラッグを `robinhood_mainnet` に変えるだけです。同じ API key を使用します。`src/step3-mainnet.ts` はメインネットのブロック番号を読み取り、Robinhood 株式トークン向けの Data API を呼び出します：

```ts
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");

// JSON-RPC with the key: the chain slug changes.
const client = httpClient(mainnet, `${API_BASE}/${mainnet.chain}`, apiKey);
console.log(`mainnet block: ${await client.getBlockNumber()}`);

// Data API: Robinhood stock-token leaderboard (see the Robinhood Chain guide).
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 での同じリーダーボードエンドポイント：

```bash
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` と報告されます。リクエストパラメーターおよびレスポンスのエンベロープについては、[トークン化株式ガイド](https://docs.blockvectra.com/en/guides/stocks/)を参照してください。

## API から制限と機能を取得する

`GET /v1/chains` を読み取って、リアルタイムの `jsonrpc`、`data`、`ws`、`subscriptions` の各フラグ、許可された JSON-RPC メソッド、ログブロック範囲、状態保持期間、各チェーンの公開エンドポイントを確認します。現在の価格とプランの制限については `GET /v1/plans` を確認してください。

```bash
curl -s "https://api.blockvectra.com/v1/chains"
```

## 関連ガイド

* [テストネットの RPC URL と制限を確認する](https://blockvectra.com/en/chains/robinhood_testnet/)。
* トランザクションを送信する前に、[テストネットフォーセットからテスト用 ETH を取得](https://docs.blockvectra.com/en/guides/robinhood-testnet-faucet/)してください。

## 次のステップ

* [データセット一覧を参照する](https://blockvectra.com/en/data/)：BlockVectra がインデックスしているすべてのデータセットを確認できます。
* [無料プランと料金体系を確認する](https://blockvectra.com/en/pricing/#free)：アカウントに含まれる内容を確認できます。
* [コンソールにログインする](https://console.blockvectra.com/login/?next=%2Fkeys%2F)：API key を作成します。
