# RPC examples

> Working code for the Supanode Robinhood Chain node: curl, ethers v6, viem, web3.py and wscat over HTTPS and WebSocket with x-token auth. Every example was run against the live endpoint.

Every snippet here was run against the node on 24 September 2026 with ethers 6.17, viem 2.56, web3.py 8.0, ws 8.21 and wscat 6.1. Replace `YOUR_TOKEN` with your token.

<Note>
**Two headers on every HTTPS call:** `x-token: YOUR_TOKEN` and `Content-Type: application/json`. Most libraries send the second one for you. web3.py does not when you pass your own headers - see below.
</Note>

## curl

<Tabs>
  <Tab title="eth_blockNumber">
    ```bash
    curl -s https://ny01.rh.supanode.xyz:8545 \
      -H "x-token: YOUR_TOKEN" -H "Content-Type: application/json" \
      -d '{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}'
    ```

    ```json
    {"jsonrpc":"2.0","id":1,"result":"0x43eda90"}
    ```
  </Tab>
  <Tab title="eth_getBalance">
    ```bash
    curl -s https://ny01.rh.supanode.xyz:8545 \
      -H "x-token: YOUR_TOKEN" -H "Content-Type: application/json" \
      -d '{"jsonrpc":"2.0","id":1,"method":"eth_getBalance","params":["0x0000000000000000000000000000000000000000","latest"]}'
    ```

    ```json
    {"jsonrpc":"2.0","id":1,"result":"0x453bfc29fc125cd9"}
    ```
  </Tab>
  <Tab title="eth_getLogs">
    ```bash
    curl -s https://ny01.rh.supanode.xyz:8545 \
      -H "x-token: YOUR_TOKEN" -H "Content-Type: application/json" \
      -d '{"jsonrpc":"2.0","id":1,"method":"eth_getLogs","params":[{"address":"0x0bd7d308f8e1639fab988df18a8011f41eacad73","fromBlock":"0x43f5f3f","toBlock":"0x43f5f49"}]}'
    ```

    Block numbers are hex. Replace them with a recent range and keep it short: see [Limits](https://supanode.xyz/docs/robinhood/rpc/limits#eth_getlogs).
  </Tab>
</Tabs>

## ethers v6 (Node)

ethers takes the header through a `FetchRequest` for HTTPS, and through a `ws` socket factory for WebSocket.

```js
import { ethers } from "ethers";
import WebSocket from "ws";

// HTTPS
const req = new ethers.FetchRequest("https://ny01.rh.supanode.xyz:8545");
req.setHeader("x-token", "YOUR_TOKEN");
const provider = new ethers.JsonRpcProvider(req);
console.log(await provider.getBlockNumber());        // 71228555
console.log((await provider.getNetwork()).chainId);  // 4663n

// WebSocket
const ws = new ethers.WebSocketProvider(() =>
  new WebSocket("wss://ny01.rh.supanode.xyz:8546", { headers: { "x-token": "YOUR_TOKEN" } }));
ws.on("block", (n) => console.log("new block", n));
```

## viem (Node)

```js
import { createPublicClient, http, formatEther } from "viem";

const client = createPublicClient({
  transport: http("https://ny01.rh.supanode.xyz:8545", {
    fetchOptions: { headers: { "x-token": "YOUR_TOKEN" } },
  }),
});
console.log(await client.getChainId());      // 4663
console.log(await client.getBlockNumber());  // 71261467n
console.log(formatEther(await client.getBalance({
  address: "0x0000000000000000000000000000000000000000",
})));
```

<Warning>
**viem's `webSocket()` transport has no option for headers.** Without the header the handshake is rejected and viem reports "An unknown RPC error occurred". For WebSocket use ethers or web3.py. If you must stay on viem, the workaround below replaces the global `WebSocket` in Node with a `ws` subclass that adds the header. Import viem after the override.
</Warning>

```js
import WS from "ws";

globalThis.WebSocket = class extends WS {
  constructor(url, protocols) {
    super(url, protocols, { headers: { "x-token": "YOUR_TOKEN" } });
  }
};

const { createPublicClient, webSocket } = await import("viem");
const client = createPublicClient({ transport: webSocket("wss://ny01.rh.supanode.xyz:8546") });
client.watchBlocks({ onBlock: (b) => console.log("new block", b.number) });
```

## web3.py 8

```python
import asyncio
from web3 import Web3, AsyncWeb3, WebSocketProvider

# HTTPS: your headers replace web3.py's defaults, so Content-Type goes in too
w3 = Web3(Web3.HTTPProvider(
    "https://ny01.rh.supanode.xyz:8545",
    request_kwargs={"headers": {"Content-Type": "application/json", "x-token": "YOUR_TOKEN"}},
))
print(w3.eth.chain_id, w3.eth.block_number)  # 4663 71261842

# WebSocket: newHeads
async def main():
    async with AsyncWeb3(WebSocketProvider(
        "wss://ny01.rh.supanode.xyz:8546",
        websocket_kwargs={"additional_headers": {"x-token": "YOUR_TOKEN"}},
    )) as w3:
        await w3.eth.subscribe("newHeads")
        async for msg in w3.socket.process_subscriptions():
            print("new block", msg["result"]["number"])

asyncio.run(main())
```

Without `Content-Type` in `request_kwargs` the node answers `415 Unsupported Media Type`.

## wscat

```bash
npx wscat -c wss://ny01.rh.supanode.xyz:8546 -H "x-token: YOUR_TOKEN"
```

Then subscribe:

```
> {"jsonrpc":"2.0","id":1,"method":"eth_subscribe","params":["newHeads"]}
< {"jsonrpc":"2.0","id":1,"result":"0x8b9e63df4cf5b0919e691cbf5e3e1ddf"}
< {"jsonrpc":"2.0","method":"eth_subscription","params":{"subscription":"0x8b9e...","result":{"number":"0x...", ...}}}
```

A filtered logs subscription on the same connection:

```
> {"jsonrpc":"2.0","id":2,"method":"eth_subscribe","params":["logs",{"address":"0x0bd7d308f8e1639fab988df18a8011f41eacad73"}]}
```

## L2 block number from a contract call

Solidity `block.number` returns a parent-chain number on Arbitrum. The ArbSys precompile at `0x64` gives the L2 block:

```bash
curl -s https://ny01.rh.supanode.xyz:8545 \
  -H "x-token: YOUR_TOKEN" -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"eth_call","params":[{"to":"0x0000000000000000000000000000000000000064","data":"0xa3b1b31d"},"latest"]}'
```

`0xa3b1b31d` is the selector of `arbBlockNumber()`.
