Robinhood Chain 테스트넷 RPC 시작하기: 키 없는 읽기, WebSocket 로그, 메인넷 전환
Robinhood Chain 테스트넷 RPC로 시작하기: 퍼블릭 RPC URL, 키 없는 읽기, API key를 사용한 WebSocket 로그 구독, 수도꼭지(faucet) 접근 및 동일한 키로 메인넷 전환.
BlockVectra는 Robinhood Chain 테스트넷(Chain ID 46630)에 대해 JSON-RPC 및 WebSocket을 지원하며, 동일한 API key는 Robinhood Chain 메인넷에서도 작동합니다. 이 가이드는 Robinhood Chain 테스트넷 스타터 템플릿의 세 단계를 따릅니다: 키 없이 테스트넷 읽기, 프로그래밍 방식으로 계정 생성 및 로그 스트리밍, 동일한 키를 메인넷으로 전환하기. 엔드포인트 파라미터, 메서드 정책 및 토큰화 주식 데이터에 대해서는 Robinhood Chain 가이드를 참조하세요.
템플릿 실행
git clone https://github.com/blockvectra/robinhood-testnet-starter
cd robinhood-testnet-starter
npm install
npm run typecheckNode.js 18 이상이 필요합니다. 세 가지 스크립트는 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 Chain 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은 16진수 Chain ID이며 10진수로는 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는 Chain ID, 최신 블록 및 주소 잔액을 읽습니다:
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 무료 제공 — 신용카드 불필요.
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 목록을 확인합니다:
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단계: 동일한 키를 메인넷으로 전환
메인넷 전환은 단어 하나만 바꾸면 됩니다: 슬러그 robinhood_mainnet과 동일한 API 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");
// 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을 사용한 동일한 리더보드 엔드포인트:
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로 보고됩니다. 요청 파라미터 및 응답 외층 구조는 토큰화 주식 가이드를 참조하세요.
API에서 한도 및 기능 확인
실시간 jsonrpc, data, ws, subscriptions 플래그, 허용된 JSON-RPC 메서드, 로그 블록 범위, 상태 보존 기간 및 각 체인의 퍼블릭 엔드포인트를 확인하려면 GET /v1/chains를 조회하세요. 현재 가격과 플랜 한도는 GET /v1/plans에서 확인할 수 있습니다.
curl -s "https://api.blockvectra.com/v1/chains"관련 가이드
- 테스트넷 RPC URL 및 한도 확인.
- 트랜잭션을 전송하기 전에 테스트넷 수도꼭지(faucet)에서 테스트 ETH 받기.
다음 단계
- 데이터셋 디렉터리 살펴보기: BlockVectra가 인덱싱하는 모든 데이터셋을 확인하세요.
- 무료 플랜 및 요금 확인: 계정에 포함된 혜택을 확인하세요.
- 콘솔에 로그인: API key를 생성하세요.
최종 수정일: