<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: SwiftNodes</title>
    <description>The latest articles on DEV Community by SwiftNodes (@swiftnodes).</description>
    <link>https://dev.to/swiftnodes</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4023672%2F97c1b4d7-aef6-4fc3-b459-d4c40cfe7c8c.jpg</url>
      <title>DEV Community: SwiftNodes</title>
      <link>https://dev.to/swiftnodes</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/swiftnodes"/>
    <language>en</language>
    <item>
      <title>opBNB RPC: A Developer's Guide (Chain ID 204)</title>
      <dc:creator>SwiftNodes</dc:creator>
      <pubDate>Tue, 29 Sep 2026 06:26:27 +0000</pubDate>
      <link>https://dev.to/swiftnodes/opbnb-rpc-a-developers-guide-chain-id-204-53g4</link>
      <guid>https://dev.to/swiftnodes/opbnb-rpc-a-developers-guide-chain-id-204-53g4</guid>
      <description>&lt;p&gt;opBNB is BNB Chain's layer 2. It runs the OP Stack, the same software as Optimism and Base, with one structural difference: its layer 1 is &lt;strong&gt;BNB Smart Chain&lt;/strong&gt;, not Ethereum. That one change explains most of what you will see over RPC. Blocks arrive every 250 milliseconds, &lt;code&gt;finalized&lt;/code&gt; trails the head by well under a minute, and the L1 data fee is paid to BSC.&lt;/p&gt;

&lt;p&gt;We measured it through our endpoint on 2026-09-29, and found one trap on the way that is worth the whole post: a public endpoint reporting a &lt;code&gt;finalized&lt;/code&gt; block nearly four hours old.&lt;/p&gt;

&lt;h2&gt;
  
  
  The essentials
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;opBNB mainnet&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Chain ID&lt;/td&gt;
&lt;td&gt;204 (&lt;code&gt;0xcc&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Settles to&lt;/td&gt;
&lt;td&gt;BNB Smart Chain&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gas token&lt;/td&gt;
&lt;td&gt;BNB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Block time&lt;/td&gt;
&lt;td&gt;250 ms (1,000 blocks in 250 s; 79 &lt;code&gt;newHeads&lt;/code&gt; in 20 s)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gas limit&lt;/td&gt;
&lt;td&gt;100,000,000 per block&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;eth_gasPrice&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0.001 gwei&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;safe&lt;/code&gt; / &lt;code&gt;finalized&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;about 30 to 50 s behind &lt;code&gt;latest&lt;/code&gt; on healthy nodes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;eth_getLogs&lt;/code&gt; range&lt;/td&gt;
&lt;td&gt;50,000 blocks at most (&lt;code&gt;exceed maximum block range: 50000&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recent state&lt;/td&gt;
&lt;td&gt;about the last 100 blocks on full nodes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tracing&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;debug_traceBlockByNumber&lt;/code&gt; and &lt;code&gt;eth_getBlockReceipts&lt;/code&gt; yes; &lt;code&gt;trace_*&lt;/code&gt; no&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Node client&lt;/td&gt;
&lt;td&gt;opBNB's &lt;code&gt;op-geth&lt;/code&gt; (&lt;code&gt;Geth/v0.5.9-stable&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;It is EVM-equivalent. viem, ethers, Foundry and Hardhat work with the chain ID set to 204, and viem ships an &lt;code&gt;opBNB&lt;/code&gt; chain definition.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four blocks a second
&lt;/h2&gt;

&lt;p&gt;At 250 ms per block, habits from Ethereum stop scaling:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Polling &lt;code&gt;latest&lt;/code&gt; every block is 345,600 calls a day.&lt;/strong&gt; Subscribe to &lt;code&gt;newHeads&lt;/code&gt; or, better, to the &lt;code&gt;logs&lt;/code&gt; you actually need. The &lt;a href="https://swiftnodes.io/blog/eth-subscribe-logs" rel="noopener noreferrer"&gt;&lt;code&gt;eth_subscribe&lt;/code&gt; logs guide&lt;/a&gt; shows the pattern.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Block counts are not time.&lt;/strong&gt; "Wait 12 confirmations" is 3 seconds. Use block tags or timestamps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The 50,000-block &lt;code&gt;eth_getLogs&lt;/code&gt; cap is about 3.5 hours.&lt;/strong&gt; A one-day backfill is seven requests; a month is about 210. The error message states the limit, so a client can read it and adapt; &lt;a href="https://swiftnodes.io/blog/eth-getlogs-range-caps" rel="noopener noreferrer"&gt;eth_getLogs range caps&lt;/a&gt; covers chunking.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Finality, and the stale-head trap
&lt;/h2&gt;

&lt;p&gt;Because BSC finalizes its own blocks within seconds, opBNB's &lt;code&gt;finalized&lt;/code&gt; head moves quickly. On the official BNB Chain endpoint, dRPC and NodeReal, &lt;code&gt;finalized&lt;/code&gt; trailed the newest block by 27 to 51 seconds across repeated checks.&lt;/p&gt;

&lt;p&gt;One widely used public endpoint told a different story. On about half of our requests it returned a &lt;code&gt;finalized&lt;/code&gt; block &lt;strong&gt;13,631 seconds, about 3.8 hours, old&lt;/strong&gt;, and a fresh one on the rest: a load-balanced pool with some backends stuck. Its WebSocket did the same, at 15,107 seconds. That endpoint was the primary opBNB upstream behind our own service, so we moved opBNB to the official endpoint and dRPC that morning; our endpoint now returns &lt;code&gt;finalized&lt;/code&gt; 32 to 38 seconds behind on every request.&lt;/p&gt;

&lt;p&gt;The lesson is general. A node that serves a stale &lt;code&gt;finalized&lt;/code&gt; doesn't error. A deposit flow that waits for &lt;code&gt;finalized&lt;/code&gt; just waits, for hours, and a load-balanced endpoint can even make &lt;code&gt;finalized&lt;/code&gt; appear to jump backwards between two calls. Check the tag's timestamp, not just its number:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createPublicClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;viem&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;opBNB&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;viem/chains&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createPublicClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;opBNB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;http&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://rpc.swiftnodes.io/rpc/opbnb?key=YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;latest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;finalized&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBlock&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;blockTag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;latest&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBlock&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;blockTag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;finalized&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lagSeconds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;latest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;finalized&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lagSeconds&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// opBNB normally finalizes in under a minute: this node's view of BSC is stuck&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`finalized head is &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lagSeconds&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;s behind`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same idea for the head itself is in &lt;a href="https://swiftnodes.io/blog/is-your-rpc-node-synced-stale-endpoint" rel="noopener noreferrer"&gt;is your RPC node synced?&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fees: the L1 part matters here
&lt;/h2&gt;

&lt;p&gt;opBNB receipts carry the OP Stack L1 fee fields (&lt;code&gt;l1Fee&lt;/code&gt;, &lt;code&gt;l1GasPrice&lt;/code&gt;, &lt;code&gt;l1GasUsed&lt;/code&gt;, &lt;code&gt;l1BlobBaseFee&lt;/code&gt; and the scalars), and on opBNB they are not negligible. Across 12 recent user transactions:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Total&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;L2 execution fee (&lt;code&gt;gasUsed × effectiveGasPrice&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;289,556,149,631 wei&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;L1 data fee (&lt;code&gt;l1Fee&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;171,390,813,185 wei&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;L1 share of the total&lt;/td&gt;
&lt;td&gt;37%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That is the opposite of &lt;a href="https://swiftnodes.io/blog/katana-rpc-developer-guide" rel="noopener noreferrer"&gt;Katana&lt;/a&gt;, where the L1 part was 0.2%. Both are tiny in absolute terms: the sample averaged about 0.00000004 BNB per transaction, a small fraction of a cent. But if you estimate cost as &lt;code&gt;gasUsed × gasPrice&lt;/code&gt; alone, you will be about a third low. Use the receipt's &lt;code&gt;l1Fee&lt;/code&gt;, or ask the &lt;code&gt;GasPriceOracle&lt;/code&gt; predeploy at &lt;code&gt;0x420000000000000000000000000000000000000F&lt;/code&gt; with &lt;code&gt;getL1Fee(bytes)&lt;/code&gt; before sending; for 100 bytes of calldata it returned 8,840,000,065 wei.&lt;/p&gt;

&lt;h2&gt;
  
  
  State and tracing
&lt;/h2&gt;

&lt;p&gt;Full opBNB nodes keep very little history. Through our endpoint, &lt;code&gt;eth_getBalance&lt;/code&gt; worked up to about 90 blocks back and failed with &lt;code&gt;missing trie node&lt;/code&gt; beyond roughly 127 blocks: about 30 seconds of state. The official endpoint and dRPC also refused a state query 100,000 blocks back. If you need balances or contract state at older blocks, you need an archive node; plan for that before you build a backfill around &lt;code&gt;eth_call&lt;/code&gt; at historical heights.&lt;/p&gt;

&lt;p&gt;For execution detail, &lt;code&gt;debug_traceBlockByNumber&lt;/code&gt; with &lt;code&gt;callTracer&lt;/code&gt; works and &lt;code&gt;eth_getBlockReceipts&lt;/code&gt; returns a block's receipts in one call. The &lt;code&gt;trace_*&lt;/code&gt; namespace (&lt;code&gt;trace_block&lt;/code&gt;, &lt;code&gt;trace_filter&lt;/code&gt;) is not available, so tools built on &lt;code&gt;trace_filter&lt;/code&gt; need to switch to &lt;code&gt;debug_*&lt;/code&gt; tracing.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you're coming from BSC or Optimism
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;From BSC:&lt;/strong&gt; same token for gas (BNB) and much of the same ecosystem, but a separate chain with its own chain ID, 250 ms blocks instead of BSC's, and OP Stack L1 fees on top of execution. The &lt;a href="https://swiftnodes.io/blog/bnb-smart-chain-rpc" rel="noopener noreferrer"&gt;BNB Smart Chain RPC guide&lt;/a&gt; covers the L1 side.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;From Optimism or Base:&lt;/strong&gt; the same predeploys at &lt;code&gt;0x4200…&lt;/code&gt;, the same deposit transactions (type &lt;code&gt;0x7e&lt;/code&gt;) at the start of each block, and the same &lt;code&gt;safe&lt;/code&gt;/&lt;code&gt;finalized&lt;/code&gt; semantics, but finality measured in seconds rather than the minutes an Ethereum-settled rollup takes. The sequencer's role is the same as described in &lt;a href="https://swiftnodes.io/blog/what-is-a-sequencer" rel="noopener noreferrer"&gt;what is a sequencer&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Chain ID 204, BNB for gas,&lt;/strong&gt; OP Stack settling to BSC.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;250 ms blocks:&lt;/strong&gt; subscribe, don't poll; count time, not blocks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;finalized&lt;/code&gt; should be under a minute old.&lt;/strong&gt; If it's hours old, your node's view of BSC is stuck: check timestamps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The L1 data fee was 37% of the total&lt;/strong&gt; in our sample; include &lt;code&gt;l1Fee&lt;/code&gt; in cost estimates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;eth_getLogs&lt;/code&gt; caps at 50,000 blocks,&lt;/strong&gt; full nodes keep about 100 blocks of state, and &lt;code&gt;debug_*&lt;/code&gt; works while &lt;code&gt;trace_*&lt;/code&gt; doesn't.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;opBNB HTTP and WebSocket endpoints are on our &lt;a href="https://swiftnodes.io/opbnb-rpc" rel="noopener noreferrer"&gt;opBNB RPC page&lt;/a&gt;, with the live block height. The same key covers 83 chains, and &lt;a href="https://swiftnodes.io/" rel="noopener noreferrer"&gt;SwiftNodes has a free tier&lt;/a&gt; to try it.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://swiftnodes.io/blog/opbnb-rpc-developer-guide" rel="noopener noreferrer"&gt;SwiftNodes blog&lt;/a&gt;. SwiftNodes provides flat-rate multi-chain RPC endpoints — HTTP + WebSocket, 75+ chains, no per-request metering. &lt;a href="https://swiftnodes.io/" rel="noopener noreferrer"&gt;Grab a free key&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opbnb</category>
      <category>blockchain</category>
      <category>web3</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Shibarium RPC: A Developer's Guide (Chain ID 109)</title>
      <dc:creator>SwiftNodes</dc:creator>
      <pubDate>Mon, 28 Sep 2026 06:21:14 +0000</pubDate>
      <link>https://dev.to/swiftnodes/shibarium-rpc-a-developers-guide-chain-id-109-1dni</link>
      <guid>https://dev.to/swiftnodes/shibarium-rpc-a-developers-guide-chain-id-109-1dni</guid>
      <description>&lt;p&gt;Shibarium is the Shiba Inu ecosystem's own chain, live since August 2023. If you have built on Polygon PoS, you already know most of it: Shibarium runs the same two-layer design, with &lt;strong&gt;Bor&lt;/strong&gt; producing blocks and executing the EVM, and &lt;strong&gt;Heimdall&lt;/strong&gt; validators writing checkpoints to Ethereum. That inheritance explains almost every quirk you will hit over RPC, including a native token that is also a contract and a &lt;code&gt;safe&lt;/code&gt; block tag that doesn't answer.&lt;/p&gt;

&lt;p&gt;This guide covers what we measured through our Shibarium endpoint on 2026-09-28.&lt;/p&gt;

&lt;h2&gt;
  
  
  The essentials
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Shibarium mainnet&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Chain ID&lt;/td&gt;
&lt;td&gt;109 (&lt;code&gt;0x6d&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gas token&lt;/td&gt;
&lt;td&gt;BONE, 18 decimals&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;First block&lt;/td&gt;
&lt;td&gt;2023-08-05&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Block time&lt;/td&gt;
&lt;td&gt;about 7 s (6.97 s averaged over the last 10,000 blocks)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gas limit&lt;/td&gt;
&lt;td&gt;20,000,000 per block&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;eth_gasPrice&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0.40 gwei (in BONE)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;finalized&lt;/code&gt; head&lt;/td&gt;
&lt;td&gt;13 to 17 blocks (65 to 85 s) behind &lt;code&gt;latest&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;safe&lt;/code&gt; head&lt;/td&gt;
&lt;td&gt;returned an error on every attempt&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;eth_getLogs&lt;/code&gt; range&lt;/td&gt;
&lt;td&gt;a 1,000,000-block range was accepted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Historical state&lt;/td&gt;
&lt;td&gt;full archive, back to block 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tracing&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;debug_traceBlockByNumber&lt;/code&gt; yes; &lt;code&gt;trace_*&lt;/code&gt; no&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;It is EVM-compatible: Foundry, Hardhat, viem and ethers work with the chain ID set to 109. The differences are in what Bor adds on top.&lt;/p&gt;

&lt;h2&gt;
  
  
  BONE is gas, and also a contract
&lt;/h2&gt;

&lt;p&gt;Fees are paid in BONE, the native token. As on Polygon PoS, the native token is also exposed as an ERC-20-style contract at a fixed system address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cast call 0x0000000000000000000000000000000000001010 &lt;span class="s2"&gt;"symbol()(string)"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--rpc-url&lt;/span&gt; &lt;span class="s2"&gt;"https://rpc.swiftnodes.io/rpc/shibarium?key=YOUR_API_KEY"&lt;/span&gt;
&lt;span class="c"&gt;# "BONE"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The contract at &lt;code&gt;0x…1010&lt;/code&gt; returns name "Bone Token", symbol "BONE" and 18 decimals. Two practical consequences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A wallet's BONE balance is &lt;code&gt;eth_getBalance&lt;/code&gt;.&lt;/strong&gt; &lt;code&gt;balanceOf&lt;/code&gt; on &lt;code&gt;0x…1010&lt;/code&gt; returns the same number (we compared them), because the contract mirrors native balances rather than holding separate ones. An indexer that adds the two together, or treats &lt;code&gt;0x…1010&lt;/code&gt; as just another ERC-20, will double-count.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Native transfers emit logs from &lt;code&gt;0x…1010&lt;/code&gt;.&lt;/strong&gt; A plain BONE transfer we checked produced two logs from that address: &lt;code&gt;LogTransfer&lt;/code&gt; (topic &lt;code&gt;0xe6497e3e…&lt;/code&gt;) for the value and &lt;code&gt;LogFeeTransfer&lt;/code&gt; (topic &lt;code&gt;0x4dfe1bbb…&lt;/code&gt;) for the gas fee. Filter &lt;code&gt;eth_getLogs&lt;/code&gt; on &lt;code&gt;LogTransfer&lt;/code&gt; and you can follow native BONE movements without traces. That is by design on Bor chains.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Don't confuse the gas token with the Ethereum-side tokens. SHIB, LEASH and BONE on Ethereum are ordinary ERC-20s; on Shibarium, only BONE is native.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finality: use &lt;code&gt;finalized&lt;/code&gt;, not &lt;code&gt;safe&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Bor finalizes blocks through milestones agreed by Heimdall validators. In our samples, &lt;code&gt;finalized&lt;/code&gt; trailed the newest block by 13 to 17 blocks, a bit over a minute. That is the tag to wait for before you treat a deposit as settled:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createPublicClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defineChain&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;viem&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shibarium&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineChain&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;109&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Shibarium&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;nativeCurrency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Bone&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;BONE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;decimals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;rpcUrls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://rpc.swiftnodes.io/rpc/shibarium?key=YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createPublicClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;shibarium&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;http&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;finalized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBlock&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;blockTag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;finalized&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;safe&lt;/code&gt; tag is a different story: every request for it failed through our endpoint. Code written for Ethereum or OP Stack chains often asks for &lt;code&gt;safe&lt;/code&gt; as a middle ground; on Shibarium, drop straight to &lt;code&gt;finalized&lt;/code&gt;. The general model behind these tags is in &lt;a href="https://swiftnodes.io/blog/l2-finality-soft-vs-hard" rel="noopener noreferrer"&gt;L2 finality: soft vs hard&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Finality on Shibarium is not the same as settlement on Ethereum. Heimdall checkpoints batches of blocks to Ethereum, and bridge withdrawals wait for them: the Shib team put bridge withdrawals at about 45 minutes after a May 2024 upgrade (&lt;a href="https://news.shib.io/2024/05/29/shibarium-bridge-enhancements-decrease-bone-transfer-time-from-7-days-to-45-minutes/" rel="noopener noreferrer"&gt;Shib Daily&lt;/a&gt;), down from up to seven days for BONE.&lt;/p&gt;

&lt;h2&gt;
  
  
  Logs, history and tracing
&lt;/h2&gt;

&lt;p&gt;Three things are easier on Shibarium than on most chains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;eth_getLogs&lt;/code&gt; over big ranges.&lt;/strong&gt; Our endpoint accepted a single address-filtered query over 1,000,000 blocks, about 80 days of chain. That still isn't a reason to skip pagination: a busy contract over that range can return more logs than you want in one response. The trade-offs are in &lt;a href="https://swiftnodes.io/blog/eth-getlogs-range-caps" rel="noopener noreferrer"&gt;eth_getLogs range caps&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full history.&lt;/strong&gt; &lt;code&gt;eth_getBalance&lt;/code&gt; answered at 128, 10,000 and 1,000,000 blocks back, and at block 1, so historical state queries work without a separate archive endpoint. See &lt;a href="https://swiftnodes.io/blog/full-node-vs-archive-node" rel="noopener noreferrer"&gt;full node vs archive node&lt;/a&gt; for what that buys you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;debug_*&lt;/code&gt; tracing.&lt;/strong&gt; &lt;code&gt;debug_traceBlockByNumber&lt;/code&gt; with &lt;code&gt;callTracer&lt;/code&gt; works.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One gap: the Parity-style &lt;code&gt;trace_*&lt;/code&gt; methods (&lt;code&gt;trace_block&lt;/code&gt;, &lt;code&gt;trace_filter&lt;/code&gt;) are not available. Tools that depend on &lt;code&gt;trace_filter&lt;/code&gt; for internal transactions need to switch to &lt;code&gt;debug_traceTransaction&lt;/code&gt; with &lt;code&gt;callTracer&lt;/code&gt;, one transaction at a time. What that costs is covered in &lt;a href="https://swiftnodes.io/blog/debug-tracetransaction-cost" rel="noopener noreferrer"&gt;the real cost of debug_traceTransaction&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Bor-specific methods are there as well. &lt;code&gt;bor_getAuthor&lt;/code&gt; returns a block's producer, and &lt;code&gt;bor_getCurrentValidators&lt;/code&gt; returned three validators for the current span when we checked. That is a small active producer set, worth knowing if your application's security assumptions depend on the chain's decentralisation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security history you should know
&lt;/h2&gt;

&lt;p&gt;In September 2025 an attacker used a large BONE stake to push three fraudulent checkpoints into the Ethereum contracts that secure the Shibarium bridge, and drained about 224.57 ETH and 92.6 billion SHIB, around $2.4 million at the time. Heimdall halted the chain when its state no longer matched the checkpoints on Ethereum, and the network was later restored with new safeguards, including blacklisting on the Plasma bridge (&lt;a href="https://www.mexc.com/en-NG/news/shibarium-survives-bridge-hack-with-4-6m-bone-recovery-and-new-safeguards/119119" rel="noopener noreferrer"&gt;MEXC News&lt;/a&gt;, &lt;a href="https://www.tradingview.com/news/cryptonews:4b9330752094b:0-shibarium-reboots-after-4m-hack-pledges-user-refunds-here-s-the-plan/" rel="noopener noreferrer"&gt;TradingView&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;For RPC users, the lesson is practical: a chain can halt. A health check that only asks whether the RPC answers will not notice: a halted chain's endpoints keep answering, with the same block. Compare the latest block's timestamp with the clock instead. At a 7-second block time, a head that hasn't moved in a minute is worth an alert.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you're coming from Polygon PoS
&lt;/h2&gt;

&lt;p&gt;Most of what you know transfers directly: the &lt;code&gt;0x…1010&lt;/code&gt; native-token contract, &lt;code&gt;bor_*&lt;/code&gt; methods, milestone finality and checkpoint-based withdrawals all work the same way. The differences are the gas token (BONE, not POL), the chain ID, a slower block time than Polygon's 2 seconds, and a much smaller validator set. Our &lt;a href="https://swiftnodes.io/blog/polygon-rpc" rel="noopener noreferrer"&gt;Polygon RPC guide&lt;/a&gt; covers the shared architecture from the Polygon side.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Chain ID 109, BONE for gas,&lt;/strong&gt; Bor and Heimdall under the hood.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Native BONE is also a contract at &lt;code&gt;0x…1010&lt;/code&gt;:&lt;/strong&gt; use &lt;code&gt;eth_getBalance&lt;/code&gt; for balances, and expect native-transfer logs from that address.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use &lt;code&gt;finalized&lt;/code&gt;&lt;/strong&gt; (about a minute behind); &lt;code&gt;safe&lt;/code&gt; doesn't answer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wide &lt;code&gt;eth_getLogs&lt;/code&gt; ranges, full archive, &lt;code&gt;debug_*&lt;/code&gt; tracing;&lt;/strong&gt; no &lt;code&gt;trace_*&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Watch block timestamps,&lt;/strong&gt; not just block numbers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Shibarium HTTP and WebSocket endpoints, with the live block height, are on our &lt;a href="https://swiftnodes.io/shibarium-rpc" rel="noopener noreferrer"&gt;Shibarium RPC page&lt;/a&gt;. The same key covers 83 chains, and &lt;a href="https://swiftnodes.io/" rel="noopener noreferrer"&gt;SwiftNodes has a free tier&lt;/a&gt; to try it.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://swiftnodes.io/blog/shibarium-rpc-developer-guide" rel="noopener noreferrer"&gt;SwiftNodes blog&lt;/a&gt;. SwiftNodes provides flat-rate multi-chain RPC endpoints — HTTP + WebSocket, 75+ chains, no per-request metering. &lt;a href="https://swiftnodes.io/" rel="noopener noreferrer"&gt;Grab a free key&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>shibarium</category>
      <category>blockchain</category>
      <category>web3</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Katana RPC: A Developer's Guide to the DeFi-First L2</title>
      <dc:creator>SwiftNodes</dc:creator>
      <pubDate>Sun, 27 Sep 2026 07:28:19 +0000</pubDate>
      <link>https://dev.to/swiftnodes/katana-rpc-a-developers-guide-to-the-defi-first-l2-5b7a</link>
      <guid>https://dev.to/swiftnodes/katana-rpc-a-developers-guide-to-the-defi-first-l2-5b7a</guid>
      <description>&lt;p&gt;Katana looks like every other OP Stack chain from the RPC side: the same &lt;code&gt;eth_*&lt;/code&gt; methods, the same predeploys at &lt;code&gt;0x4200…&lt;/code&gt;, the same L1 fee fields on receipts. Two things underneath are different, and both change how you write code against it. Blocks come every second, not every two. And Katana settles with &lt;strong&gt;ZK validity proofs&lt;/strong&gt; instead of a seven-day fraud-proof window, so "finalized" arrives in minutes rather than a week.&lt;/p&gt;

&lt;p&gt;Katana is a DeFi-focused Ethereum L2 incubated by Polygon Labs and GSR. It opened its public mainnet in June 2025. Per &lt;a href="https://l2beat.com/scaling/projects/katana" rel="noopener noreferrer"&gt;L2BEAT&lt;/a&gt;, it runs the Agglayer CDK in its OP Stack configuration: an OP Stack chain proven with &lt;strong&gt;OP Succinct&lt;/strong&gt; (SP1 validity proofs), using &lt;strong&gt;Agglayer&lt;/strong&gt; as its canonical bridge. Bridged assets go through &lt;strong&gt;VaultBridge&lt;/strong&gt;, which puts deposits into yield-generating vaults on Ethereum and routes the yield back into Katana's ecosystem.&lt;/p&gt;

&lt;p&gt;This guide covers what we measured through our endpoint on 2026-09-27, and what it means for your code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The essentials
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Katana mainnet&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Chain ID&lt;/td&gt;
&lt;td&gt;747474 (&lt;code&gt;0xb67d2&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gas token&lt;/td&gt;
&lt;td&gt;ETH&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Block time&lt;/td&gt;
&lt;td&gt;1.00 s (1,000 blocks in 1,000 s; 20 &lt;code&gt;newHeads&lt;/code&gt; in 20 s, no gaps)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gas limit&lt;/td&gt;
&lt;td&gt;120,000,000 per block&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Base fee&lt;/td&gt;
&lt;td&gt;0.001 gwei&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;safe&lt;/code&gt; head&lt;/td&gt;
&lt;td&gt;341 blocks (about 5.7 minutes) behind &lt;code&gt;latest&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;finalized&lt;/code&gt; head&lt;/td&gt;
&lt;td&gt;1,589 blocks (about 26.5 minutes) behind &lt;code&gt;latest&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Node client&lt;/td&gt;
&lt;td&gt;&lt;code&gt;conduit-op-reth/v2.3.0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;eth_getLogs&lt;/code&gt; range&lt;/td&gt;
&lt;td&gt;up to 30,000 blocks per request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Historical state&lt;/td&gt;
&lt;td&gt;full archive, back to block 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tracing&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;debug_*&lt;/code&gt; and &lt;code&gt;trace_*&lt;/code&gt; (block and transaction), plus &lt;code&gt;eth_getBlockReceipts&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;It is EVM-equivalent: Foundry, Hardhat, viem and ethers all work unchanged. Point them at the RPC and set the chain ID.&lt;/p&gt;

&lt;h2&gt;
  
  
  One block per second
&lt;/h2&gt;

&lt;p&gt;A 1-second block time is twice as fast as Base or Optimism, and it changes a few habits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Polling is expensive.&lt;/strong&gt; A loop that calls &lt;code&gt;eth_getBlockByNumber("latest")&lt;/code&gt; every block makes 86,400 requests a day. Use a WebSocket &lt;code&gt;newHeads&lt;/code&gt; subscription instead, or better, subscribe to exactly the &lt;code&gt;logs&lt;/code&gt; you care about. The &lt;a href="https://swiftnodes.io/blog/eth-subscribe-logs" rel="noopener noreferrer"&gt;&lt;code&gt;eth_subscribe&lt;/code&gt; logs guide&lt;/a&gt; shows the pattern.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Block ranges are shorter in time.&lt;/strong&gt; The 30,000-block &lt;code&gt;eth_getLogs&lt;/code&gt; limit covers about 8.3 hours of Katana history. A backfill over one month is about 87 requests, not one. The general approach for ranges is in &lt;a href="https://swiftnodes.io/blog/eth-getlogs-range-caps" rel="noopener noreferrer"&gt;eth_getLogs range caps&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Confirmation counts mean less.&lt;/strong&gt; "Wait 12 blocks" is 12 seconds on Katana. Count confirmations with block tags, not block numbers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Finality: latest, safe, finalized
&lt;/h2&gt;

&lt;p&gt;OP Stack chains expose three levels through standard block tags, and Katana's numbers are unusual:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tag&lt;/th&gt;
&lt;th&gt;Lag behind &lt;code&gt;latest&lt;/code&gt; (measured)&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;latest&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Produced by the sequencer, not yet on L1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;safe&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;about 5.7 minutes&lt;/td&gt;
&lt;td&gt;Batch data posted to Ethereum&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;finalized&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;about 26.5 minutes&lt;/td&gt;
&lt;td&gt;Batch data in a finalized Ethereum block&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;On an optimistic rollup, a withdrawal to L1 still waits seven days after &lt;code&gt;finalized&lt;/code&gt; for the fraud-proof window. Katana has no such window: its state transitions are proven with validity proofs, and Succinct puts Katana's finality at "under an hour" (&lt;a href="https://blog.succinct.xyz/case-studies/katana/" rel="noopener noreferrer"&gt;case study&lt;/a&gt;). For anything that moves value off-chain, such as crediting a deposit or releasing funds, wait for &lt;code&gt;finalized&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createPublicClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defineChain&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;viem&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;chainConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;viem/op-stack&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;katana&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineChain&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;chainConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;747474&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Katana&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;nativeCurrency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Ether&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ETH&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;decimals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;rpcUrls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://rpc.swiftnodes.io/rpc/katana?key=YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createPublicClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;katana&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;http&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;finalized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBlock&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;blockTag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;finalized&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isFinal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;txBlock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;txBlock&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;finalized&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;viem does ship a &lt;code&gt;katana&lt;/code&gt; chain in &lt;code&gt;viem/chains&lt;/code&gt;, but as of viem 2.47 it is a plain chain definition without the OP Stack formatters, so receipt fields like &lt;code&gt;l1Fee&lt;/code&gt; come back as raw hex strings. Spreading &lt;code&gt;chainConfig&lt;/code&gt; from &lt;code&gt;viem/op-stack&lt;/code&gt;, as above, adds them.&lt;/p&gt;

&lt;p&gt;The soft/hard distinction is covered in general terms in &lt;a href="https://swiftnodes.io/blog/l2-finality-soft-vs-hard" rel="noopener noreferrer"&gt;L2 finality: soft vs hard&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fees: almost all execution, almost no L1
&lt;/h2&gt;

&lt;p&gt;Katana receipts carry the standard OP Stack L1 fee fields: &lt;code&gt;l1Fee&lt;/code&gt;, &lt;code&gt;l1GasPrice&lt;/code&gt;, &lt;code&gt;l1GasUsed&lt;/code&gt;, &lt;code&gt;l1BlobBaseFee&lt;/code&gt;, &lt;code&gt;l1BaseFeeScalar&lt;/code&gt;, &lt;code&gt;l1BlobBaseFeeScalar&lt;/code&gt;. On most OP Stack chains the L1 data fee is a large part of what a user pays. On Katana it isn't. Across 12 recent user transactions:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Total&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;L2 execution fee (&lt;code&gt;gasUsed × effectiveGasPrice&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;35,373,690,000,000 wei&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;L1 data fee (&lt;code&gt;l1Fee&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;62,987,722,419 wei&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;L1 share of the total&lt;/td&gt;
&lt;td&gt;0.2%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That sample averaged about 0.000003 ETH per transaction, under a cent at current ETH prices. The practical upshot: on Katana, &lt;code&gt;eth_estimateGas&lt;/code&gt; × &lt;code&gt;eth_gasPrice&lt;/code&gt; is a good estimate of the full cost. If you want the L1 part anyway, the &lt;code&gt;chainConfig&lt;/code&gt; formatters above parse the receipt fields into bigints:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;receipt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getTransactionReceipt&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0x...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gasUsed&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;effectiveGasPrice&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;l1Fee&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or ask the &lt;code&gt;GasPriceOracle&lt;/code&gt; predeploy (&lt;code&gt;0x420000000000000000000000000000000000000F&lt;/code&gt;) with &lt;code&gt;getL1Fee(bytes)&lt;/code&gt; before sending. For 100 bytes of calldata it returned 420,810,233 wei, a fraction of a millionth of an ETH.&lt;/p&gt;

&lt;h2&gt;
  
  
  Archive and tracing
&lt;/h2&gt;

&lt;p&gt;The node behind our Katana endpoint is &lt;code&gt;op-reth&lt;/code&gt;, and it serves full history: &lt;code&gt;eth_getBalance&lt;/code&gt; worked 128, 10,000 and 1,000,000 blocks back and at block 1. The Reth tracing APIs are there too:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;debug_traceBlockByNumber&lt;/code&gt; and &lt;code&gt;debug_traceTransaction&lt;/code&gt; with &lt;code&gt;callTracer&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;trace_block&lt;/code&gt; and &lt;code&gt;trace_transaction&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;eth_getBlockReceipts&lt;/code&gt; for all receipts in one call&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With a block every second, &lt;code&gt;eth_getBlockReceipts&lt;/code&gt; matters: indexing a day of Katana receipts is 86,400 calls with it, and one call per transaction without it. For internal calls and value transfers, see &lt;a href="https://swiftnodes.io/blog/trace-filter-trace-transaction-internal-calls" rel="noopener noreferrer"&gt;trace_filter and trace_transaction&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trust model, briefly
&lt;/h2&gt;

&lt;p&gt;Katana is Stage 0 on L2BEAT. Validity proofs protect state transitions, but the sequencer and proposer are centralized. Users can force a transaction through L1 with a delay of up to 12 hours if the sequencer censors them. Contract upgrades have no exit window. None of that changes how you call the RPC, but it belongs in the risk section of anything that holds user funds on the chain. The sequencer's role in general is explained in &lt;a href="https://swiftnodes.io/blog/what-is-a-sequencer" rel="noopener noreferrer"&gt;what is a sequencer&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Chain ID 747474, ETH for gas,&lt;/strong&gt; standard OP Stack JSON-RPC.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1-second blocks:&lt;/strong&gt; subscribe instead of polling; 30,000-block &lt;code&gt;eth_getLogs&lt;/code&gt; windows cover about 8 hours.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;finalized&lt;/code&gt; is about 26 minutes behind &lt;code&gt;latest&lt;/code&gt;,&lt;/strong&gt; and there is no seven-day withdrawal window: validity proofs settle it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fees are almost entirely L2 execution;&lt;/strong&gt; the L1 data fee was 0.2% in our sample.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full archive, &lt;code&gt;debug_*&lt;/code&gt; and &lt;code&gt;trace_*&lt;/code&gt;&lt;/strong&gt; are available.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Katana HTTP and WebSocket endpoints, with the live block height and WebSocket status, are on our &lt;a href="https://swiftnodes.io/katana-rpc" rel="noopener noreferrer"&gt;Katana RPC page&lt;/a&gt;. The same key works across 83 chains, and &lt;a href="https://swiftnodes.io/" rel="noopener noreferrer"&gt;SwiftNodes has a free tier&lt;/a&gt; to start with.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://swiftnodes.io/blog/katana-rpc-developer-guide" rel="noopener noreferrer"&gt;SwiftNodes blog&lt;/a&gt;. SwiftNodes provides flat-rate multi-chain RPC endpoints — HTTP + WebSocket, 75+ chains, no per-request metering. &lt;a href="https://swiftnodes.io/" rel="noopener noreferrer"&gt;Grab a free key&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>katana</category>
      <category>blockchain</category>
      <category>web3</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Kusama RPC: A Developer's Guide After the Asset Hub Move</title>
      <dc:creator>SwiftNodes</dc:creator>
      <pubDate>Sat, 26 Sep 2026 05:23:39 +0000</pubDate>
      <link>https://dev.to/swiftnodes/kusama-rpc-a-developers-guide-after-the-asset-hub-move-1ggh</link>
      <guid>https://dev.to/swiftnodes/kusama-rpc-a-developers-guide-after-the-asset-hub-move-1ggh</guid>
      <description>&lt;p&gt;Most Kusama RPC tutorials were written for a chain that no longer exists in the same shape. Until October 2025, the Kusama relay chain was where balances lived, where you bonded and nominated, and where governance ran. On &lt;strong&gt;October 7, 2025&lt;/strong&gt;, Kusama completed its &lt;strong&gt;Asset Hub migration&lt;/strong&gt;: balances, staking and governance moved off the relay chain to Kusama Asset Hub, through runtime upgrades, with no action required from holders.&lt;/p&gt;

&lt;p&gt;The code that broke is the code that still queries the relay chain for things that moved. It doesn't throw. It returns an empty account and a zero balance. This guide covers what the Kusama relay RPC still does, what moved, and the numbers we measured through our own endpoint on 2026-09-26.&lt;/p&gt;

&lt;h2&gt;
  
  
  The essentials
&lt;/h2&gt;

&lt;p&gt;Everything in this table was read live from &lt;code&gt;https://rpc.swiftnodes.io/rpc/kusama&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Kusama relay chain&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;system_chain&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Kusama&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Runtime (&lt;code&gt;specName&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;kusama&lt;/code&gt;, &lt;code&gt;specVersion&lt;/code&gt; 2003002, &lt;code&gt;transactionVersion&lt;/code&gt; 26&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Node&lt;/td&gt;
&lt;td&gt;Parity Polkadot 1.24.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Token&lt;/td&gt;
&lt;td&gt;KSM, &lt;strong&gt;12 decimals&lt;/strong&gt; (&lt;code&gt;system_properties&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SS58 address prefix&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Block time&lt;/td&gt;
&lt;td&gt;6.38 s average over a 90-second sample&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Finality (GRANDPA)&lt;/td&gt;
&lt;td&gt;finalized head trailed the best head by 2-3 blocks (about 13-19 s)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RPC surface&lt;/td&gt;
&lt;td&gt;120 methods: &lt;code&gt;system_*&lt;/code&gt;, &lt;code&gt;chain_*&lt;/code&gt;, &lt;code&gt;state_*&lt;/code&gt;, &lt;code&gt;author_*&lt;/code&gt;, &lt;code&gt;chainHead_v1_*&lt;/code&gt;, &lt;code&gt;grandpa_*&lt;/code&gt;, &lt;code&gt;beefy_*&lt;/code&gt;, &lt;code&gt;mmr_*&lt;/code&gt;, and others&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;eth_*&lt;/code&gt; methods&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you're coming from Ethereum, the same rules as Polkadot apply. There is no &lt;code&gt;eth_blockNumber&lt;/code&gt;, viem and ethers won't connect, and storage is SCALE-encoded rather than ABI-encoded. Our &lt;a href="https://swiftnodes.io/blog/polkadot-rpc-substrate" rel="noopener noreferrer"&gt;Polkadot RPC guide&lt;/a&gt; covers that translation; everything there carries over to Kusama except the numbers. DOT has 10 decimals, &lt;strong&gt;KSM has 12&lt;/strong&gt;. Kusama addresses use SS58 prefix 2, Polkadot's use 0, so the same public key encodes to a different address string on each network.&lt;/p&gt;

&lt;h2&gt;
  
  
  Big change: balances moved to Asset Hub
&lt;/h2&gt;

&lt;p&gt;This is the gotcha worth the whole post. We read the &lt;code&gt;Balances.TotalIssuance&lt;/code&gt; storage item and counted &lt;code&gt;System.Account&lt;/code&gt; entries on both chains:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Relay chain&lt;/th&gt;
&lt;th&gt;Kusama Asset Hub&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Balances.TotalIssuance&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;24,939 KSM&lt;/td&gt;
&lt;td&gt;18,889,082 KSM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;System.Account&lt;/code&gt; entries&lt;/td&gt;
&lt;td&gt;2,343&lt;/td&gt;
&lt;td&gt;more than 30,000 (we stopped counting)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The relay chain now accounts for roughly 0.1% of the KSM supply. If your wallet backend, tax tool or exchange integration still reads &lt;code&gt;system.account&lt;/code&gt; on the relay chain, almost every user shows a zero balance, and nothing errors to tell you so.&lt;/p&gt;

&lt;p&gt;The fix is to point balance, transfer, staking and governance queries at Asset Hub:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ApiPromise&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;WsProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@polkadot/api&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Kusama Asset Hub: balances, transfers, staking, governance&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;assetHub&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ApiPromise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WsProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;wss://kusama-asset-hub-rpc.polkadot.io&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;assetHub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;system&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;account&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YOUR_KUSAMA_ADDRESS&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;free KSM:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;free&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBigInt&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;e12&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two details trip people up here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Asset Hub's runtime is still called &lt;code&gt;statemine&lt;/code&gt;.&lt;/strong&gt; &lt;code&gt;state_getRuntimeVersion&lt;/code&gt; on Kusama Asset Hub returns &lt;code&gt;specName: "statemine"&lt;/code&gt;, the parachain's original name. If your code identifies networks by &lt;code&gt;specName&lt;/code&gt;, add it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Addresses don't change.&lt;/strong&gt; Asset Hub uses the same SS58 prefix (2) and 12 decimals, so a user's address and balance units are identical; only the chain you ask is different.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Staking follows the same rule. Bonding, nominating and unbonding are Asset Hub extrinsics now. The relay chain still has consensus work to do, so some staking-related storage remains there, but it is not where a nominator's position is managed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the relay chain RPC is still for
&lt;/h2&gt;

&lt;p&gt;The relay chain didn't become useless; it became specialised. Query it directly for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Block production and finality.&lt;/strong&gt; BABE produces relay blocks and GRANDPA finalizes them. If you need to know that something is final across the Kusama network, the relay chain's finalized head is the source.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parachain validation.&lt;/strong&gt; Which parachain blocks were backed and included is relay-chain state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bridge and light-client proofs.&lt;/strong&gt; The &lt;code&gt;beefy_*&lt;/code&gt; and &lt;code&gt;mmr_*&lt;/code&gt; methods exist for bridges and light clients that need compact proofs of relay-chain history.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validator operations.&lt;/strong&gt; Session keys are still set on the relay chain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Indexing relay events.&lt;/strong&gt; Explorers and indexers that follow the relay chain itself.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a dApp that only shows balances and sends transfers, the relay chain is now the wrong endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow finalized heads, not new heads
&lt;/h2&gt;

&lt;p&gt;Kusama's new heads can be re-organised before GRANDPA finalizes them. In our sample, finality ran 2-3 blocks behind the best head. For anything that credits a user, subscribe to finalized heads:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ApiPromise&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;WsProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@polkadot/api&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ApiPromise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WsProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;wss://rpc.swiftnodes.io/ws/kusama?key=YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribeFinalizedHeads&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;finalized&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toNumber&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same logic applies as on any chain: a WebSocket that stops delivering is not the same as a chain with no new blocks. At 6.4 seconds per block, 30 seconds without a finalized head is worth a reconnect. The pattern is in &lt;a href="https://swiftnodes.io/blog/websocket-reconnect-without-losing-events" rel="noopener noreferrer"&gt;reconnecting without losing events&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Runtime upgrades change your types
&lt;/h2&gt;

&lt;p&gt;Kusama is Polkadot's canary network, so runtime upgrades usually reach it first and more often. Every upgrade can change storage layouts, call indices and the &lt;code&gt;transactionVersion&lt;/code&gt; that signed transactions commit to. Polkadot-JS reloads metadata by itself when the runtime changes. What breaks is everything that doesn't: hand-written SCALE decoders, cached type definitions, and offline signers carrying old metadata.&lt;/p&gt;

&lt;p&gt;Watch for upgrades instead of discovering them from failed transactions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribeRuntimeVersion&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;runtime&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;specName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;specVersion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toNumber&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tx version&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transactionVersion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toNumber&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When &lt;code&gt;specVersion&lt;/code&gt; changes, reload metadata before decoding anything new. When &lt;code&gt;transactionVersion&lt;/code&gt; changes, an offline signer with old metadata will produce transactions the chain rejects.&lt;/p&gt;

&lt;h2&gt;
  
  
  History: 256 blocks of state
&lt;/h2&gt;

&lt;p&gt;Our Kusama relay endpoint is a pruned node. We asked for the &lt;code&gt;Timestamp.Now&lt;/code&gt; storage item at increasing depths:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Blocks back&lt;/th&gt;
&lt;th&gt;State available&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;64, 128, 200, 250, 256&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;300, 500, 1,000 and deeper&lt;/td&gt;
&lt;td&gt;no: &lt;code&gt;State already discarded&lt;/code&gt; (error 4003)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So state queries work for the last 256 blocks, about 27 minutes at 6.4 s per block. Blocks and headers are still available further back through &lt;code&gt;chain_getBlockHash&lt;/code&gt; and &lt;code&gt;chain_getBlock&lt;/code&gt;; it's the storage at old blocks that is pruned. For balance history, staking history or anything else that needs &lt;code&gt;state_getStorage&lt;/code&gt; at an old block hash, you need an archive node, and the migration splits it in two: account history from before October 7, 2025 is relay-chain state, and everything after is on Asset Hub. The general trade-off is in &lt;a href="https://swiftnodes.io/blog/full-node-vs-archive-node" rel="noopener noreferrer"&gt;full node vs archive node&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  New JSON-RPC API: &lt;code&gt;chainHead_v1&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Our Kusama node exposes the newer JSON-RPC interface alongside the legacy one: nine &lt;code&gt;chainHead_v1_*&lt;/code&gt; methods plus &lt;code&gt;transaction_*&lt;/code&gt; and &lt;code&gt;transactionWatch_*&lt;/code&gt;. Polkadot-JS uses the legacy &lt;code&gt;chain_*&lt;/code&gt; and &lt;code&gt;state_*&lt;/code&gt; methods. The newer &lt;a href="https://papi.how" rel="noopener noreferrer"&gt;polkadot-api&lt;/a&gt; (PAPI) client is built on &lt;code&gt;chainHead_v1&lt;/code&gt;. Both work against the same endpoint. The &lt;code&gt;archive_v1_*&lt;/code&gt; methods are not available, which matches the node being pruned.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Balances, transfers, staking and governance:&lt;/strong&gt; use Kusama Asset Hub. The relay chain holds about 0.1% of KSM.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Finality, parachain inclusion, bridge proofs, validator operations:&lt;/strong&gt; use the relay chain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;KSM has 12 decimals&lt;/strong&gt; and SS58 prefix 2, on both chains.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Credit users on finalized heads,&lt;/strong&gt; 2-3 blocks behind the best head.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscribe to runtime versions;&lt;/strong&gt; Kusama upgrades often.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Relay state is kept for 256 blocks&lt;/strong&gt; on our endpoint; older state needs an archive node.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Kusama relay-chain RPC over HTTP and WebSocket is on our &lt;a href="https://swiftnodes.io/kusama-rpc" rel="noopener noreferrer"&gt;Kusama RPC page&lt;/a&gt;, with the live block height and WebSocket status. The same key covers &lt;a href="https://swiftnodes.io/polkadot-rpc" rel="noopener noreferrer"&gt;Polkadot&lt;/a&gt; and 81 other chains. &lt;a href="https://swiftnodes.io/" rel="noopener noreferrer"&gt;SwiftNodes has a free tier&lt;/a&gt; if you want to try it.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://swiftnodes.io/blog/kusama-rpc-developer-guide" rel="noopener noreferrer"&gt;SwiftNodes blog&lt;/a&gt;. SwiftNodes provides flat-rate multi-chain RPC endpoints — HTTP + WebSocket, 75+ chains, no per-request metering. &lt;a href="https://swiftnodes.io/" rel="noopener noreferrer"&gt;Grab a free key&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>kusama</category>
      <category>blockchain</category>
      <category>web3</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>We Tested 945 Public RPC Endpoints. Half Don't Work.</title>
      <dc:creator>SwiftNodes</dc:creator>
      <pubDate>Fri, 25 Sep 2026 05:34:41 +0000</pubDate>
      <link>https://dev.to/swiftnodes/we-tested-945-public-rpc-endpoints-half-dont-work-5agb</link>
      <guid>https://dev.to/swiftnodes/we-tested-945-public-rpc-endpoints-half-dont-work-5agb</guid>
      <description>&lt;p&gt;Public RPC lists are how most developers find an endpoint: open Chainlist, copy the first URL, paste it into a config. The lists tell you an endpoint was added at some point. They do not tell you whether it works today.&lt;/p&gt;

&lt;p&gt;So we tested all of them. On 2026-09-24 we took every keyless endpoint listed on Chainlist and chainid.network for the 65 EVM chains we serve (945 third-party URLs: 800 HTTP and 145 WebSocket) and ran the same checks against each one. &lt;strong&gt;395 of the 800 HTTP endpoints answered correctly: 49.4%.&lt;/strong&gt; Of the 145 WebSocket endpoints, &lt;strong&gt;76 actually streamed a new block: 52.4%.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The full results, endpoint by endpoint, are on our &lt;a href="https://swiftnodes.io/public-rpc" rel="noopener noreferrer"&gt;public RPC leaderboard&lt;/a&gt;, re-run every week.&lt;/p&gt;

&lt;h2&gt;
  
  
  How we tested
&lt;/h2&gt;

&lt;p&gt;Every HTTP endpoint got the same sequence:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;eth_chainId&lt;/code&gt;, which must return the chain the list says it serves.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;eth_blockNumber&lt;/code&gt; three times, for median latency and the head.&lt;/li&gt;
&lt;li&gt;How far that head trails the best head seen for the chain in the same run.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;eth_getLogs&lt;/code&gt; over 1,000 and then 10,000 blocks, filtered to one address.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;eth_getBalance&lt;/code&gt; 128 and 1,000,000 blocks back, to see how much history it keeps.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Every WebSocket endpoint: &lt;code&gt;eth_chainId&lt;/code&gt;, then &lt;code&gt;eth_subscribe("newHeads")&lt;/code&gt;, and it passes only if a new block actually arrives within 15 seconds. On chains that produce blocks less often than that, an accepted subscription with no block yet is counted as inconclusive, not as a failure.&lt;/p&gt;

&lt;p&gt;A few things to keep in mind about the numbers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One server, one run.&lt;/strong&gt; Latency is from a single location. Head lag, limits and pass/fail are not location-dependent in the same way, but a flaky endpoint can pass one day and fail the next.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate limits count as failures only after backoff.&lt;/strong&gt; Calls are paced, and a rate-limited call is retried three times with increasing waits before we give up. 98 endpoints still refused. A client sending a request every few seconds might get through where our test did not; a real application under load will not.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Our own endpoint is excluded from these totals.&lt;/strong&gt; It is on the leaderboard, labelled, and measured in a separate pass (details at the end).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why half of them fail
&lt;/h2&gt;

&lt;p&gt;For the 405 HTTP endpoints that did not pass:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Failure&lt;/th&gt;
&lt;th&gt;Endpoints&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Unreachable: DNS, TLS or connection refused&lt;/td&gt;
&lt;td&gt;111&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rate limited, even after backoff&lt;/td&gt;
&lt;td&gt;98&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Other HTTP 4xx responses&lt;/td&gt;
&lt;td&gt;56&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP 5xx and other server errors&lt;/td&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Answered &lt;code&gt;eth_chainId&lt;/code&gt;, then failed &lt;code&gt;eth_blockNumber&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monthly or daily usage limit already reached&lt;/td&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Provider discontinued ("Blast API is no longer available")&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Timeout&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Other (invalid key, auth required, empty responses)&lt;/td&gt;
&lt;td&gt;40&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The biggest groups tell a simple story: &lt;strong&gt;lists outlive endpoints.&lt;/strong&gt; Domains lapse, providers shut down or move their free tier behind a signup, and the list entry stays. Thirteen URLs from one discontinued provider are still listed across chains. Others now answer "API key required", "invalid API key" or a usage-limit message, which is a reasonable business decision for the provider but a broken entry for anyone who copied it.&lt;/p&gt;

&lt;h2&gt;
  
  
  "Working" is not the same as "usable"
&lt;/h2&gt;

&lt;p&gt;Of the 395 endpoints that answered correctly:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Head within 3 blocks of the best&lt;/td&gt;
&lt;td&gt;201 (51%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;More than 100 blocks behind&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Worst lag&lt;/td&gt;
&lt;td&gt;4,375,016 blocks behind&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;eth_getLogs&lt;/code&gt; over 10,000 blocks accepted&lt;/td&gt;
&lt;td&gt;160&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Refused even 1,000 blocks&lt;/td&gt;
&lt;td&gt;164&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State readable 1,000,000 blocks back (archive depth)&lt;/td&gt;
&lt;td&gt;214&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No historical state at all (not even 128 blocks)&lt;/td&gt;
&lt;td&gt;110&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Median latency&lt;/td&gt;
&lt;td&gt;49 ms (p90: 172 ms)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The lag numbers matter most, because a lagging node does not fail — it answers, confidently, with old data. An endpoint 4.4 million blocks behind returns a perfectly valid block number; it is just from long ago. If you read balances or wait for confirmations through a node like that, your application is wrong without any error to tell you so. Always compare &lt;code&gt;eth_blockNumber&lt;/code&gt; against a second source, or check the latest block's timestamp against the clock.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;eth_getLogs&lt;/code&gt; split is the other trap. Two in five working endpoints (164 of 395) refuse a 1,000-block log query, which is small for any indexer or backfill. If your code was written against a generous endpoint and falls back to a stricter one, it will start failing with &lt;a href="https://swiftnodes.io/errors/eth-getlogs-block-range-too-large" rel="noopener noreferrer"&gt;range errors&lt;/a&gt; you never saw in development.&lt;/p&gt;

&lt;h2&gt;
  
  
  WebSockets are worse than they look
&lt;/h2&gt;

&lt;p&gt;Only 76 of 145 WebSocket endpoints delivered a block. Another 9 accepted the subscription on slow chains (Metis, Arbitrum Nova, HAQQ, Syscoin, Rootstock) that produced no block inside the window, so we count them as inconclusive. The other 60 failed:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;WebSocket failure&lt;/th&gt;
&lt;th&gt;Endpoints&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Could not connect, or timed out&lt;/td&gt;
&lt;td&gt;40&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Subscription rejected ("not available on free plan", "unsupported subscription")&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Subscription accepted, then no block delivered (on chains with frequent blocks)&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Other (chain ID errors)&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The rejected and silent groups are why "the socket connected" proves nothing. An endpoint can complete the handshake, answer &lt;code&gt;eth_blockNumber&lt;/code&gt;, accept your subscription with a valid ID, and then send nothing. Your application sees a healthy connection and waits forever. The only real test is a delivered event. We covered the client-side pattern for this in &lt;a href="https://swiftnodes.io/blog/websocket-reconnect-without-losing-events" rel="noopener noreferrer"&gt;reconnecting without losing events&lt;/a&gt;, and the specific errors are in &lt;a href="https://swiftnodes.io/errors/eth-subscribe-not-available" rel="noopener noreferrer"&gt;eth_subscribe errors explained&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which endpoints held up
&lt;/h2&gt;

&lt;p&gt;Some providers' public endpoints passed nearly everywhere they are listed. Counting HTTP endpoints that answered correctly out of those listed:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Provider&lt;/th&gt;
&lt;th&gt;Working / listed&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;dRPC&lt;/td&gt;
&lt;td&gt;50 / 51&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PublicNode&lt;/td&gt;
&lt;td&gt;29 / 29&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pocket Network&lt;/td&gt;
&lt;td&gt;26 / 27&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sentio&lt;/td&gt;
&lt;td&gt;24 / 24&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are free, keyless endpoints, and they deserve credit for staying up across dozens of chains. They still come with the usual public-endpoint limits: per-IP rate limits, tight &lt;code&gt;eth_getLogs&lt;/code&gt; caps on some chains, and shared capacity. For WebSocket, PublicNode streamed on 18 of 27 and Pocket Network on 13 of 15.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to pick a public endpoint
&lt;/h2&gt;

&lt;p&gt;Based on what failed:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Test before you trust the list.&lt;/strong&gt; Our &lt;a href="https://swiftnodes.io/tools/rpc-tester" rel="noopener noreferrer"&gt;RPC tester&lt;/a&gt; runs these checks on any endpoint from your browser: chain ID, head lag, &lt;code&gt;eth_getLogs&lt;/code&gt; range, archive depth and whether its WebSocket really streams.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check freshness, not just liveness.&lt;/strong&gt; A lagging node is worse than a dead one because it fails silently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configure two endpoints, not one.&lt;/strong&gt; Most failures above are permanent (dead domains, discontinued providers), but rate limits are not, and a second endpoint turns an outage into a retry.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Match the endpoint to the job.&lt;/strong&gt; A wallet reading the latest balance can use almost any working endpoint. An indexer needs a generous &lt;code&gt;eth_getLogs&lt;/code&gt; range and historical state; that eliminates most of the list.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Re-check periodically.&lt;/strong&gt; Every failing entry here was added to a list by someone, presumably when it worked. Endpoints decay; the list does not update itself.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Our own endpoint, measured the same way
&lt;/h2&gt;

&lt;p&gt;We ran our keyless endpoint through the same checks in a separate pass, so our rate limiter did not collide with the rest of the test. It answered correctly on 63 of 65 chains over HTTP. Over WebSocket it streamed on 45 of 65. On the other 20, either we have no WebSocket upstream for that chain or the subscription did not deliver during the test; each chain page shows our current measured WebSocket status rather than a blanket claim. Our keyless and free tiers are limited to 2 requests per second; paid plans start at 50.&lt;/p&gt;

&lt;p&gt;The per-chain pages list every endpoint with its latency, lag, limits and WebSocket result, ours included. For example: &lt;a href="https://swiftnodes.io/public-rpc/ethereum" rel="noopener noreferrer"&gt;Ethereum&lt;/a&gt; (33 of 80 listed HTTP endpoints working), &lt;a href="https://swiftnodes.io/public-rpc/base" rel="noopener noreferrer"&gt;Base&lt;/a&gt; (15 of 36) and &lt;a href="https://swiftnodes.io/public-rpc/bnb-smart-chain" rel="noopener noreferrer"&gt;BNB Smart Chain&lt;/a&gt; (36 of 47).&lt;/p&gt;

&lt;p&gt;For what free endpoints can and cannot do at the plan level, see &lt;a href="https://swiftnodes.io/blog/free-rpc-endpoints-real-limits" rel="noopener noreferrer"&gt;free RPC endpoints and their real limits&lt;/a&gt; and &lt;a href="https://swiftnodes.io/blog/public-rpc-vs-paid-rpc" rel="noopener noreferrer"&gt;public RPC vs paid RPC&lt;/a&gt;. If you want one key that works across 86 chains with the limits published and measured, &lt;a href="https://swiftnodes.io/" rel="noopener noreferrer"&gt;SwiftNodes has a free tier&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://swiftnodes.io/blog/we-tested-945-public-rpc-endpoints" rel="noopener noreferrer"&gt;SwiftNodes blog&lt;/a&gt;. SwiftNodes provides flat-rate multi-chain RPC endpoints — HTTP + WebSocket, 75+ chains, no per-request metering. &lt;a href="https://swiftnodes.io/" rel="noopener noreferrer"&gt;Grab a free key&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ethereum</category>
      <category>blockchain</category>
      <category>web3</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Dedicated Node vs Shared RPC Endpoint: When You Actually Need One</title>
      <dc:creator>SwiftNodes</dc:creator>
      <pubDate>Thu, 24 Sep 2026 05:19:44 +0000</pubDate>
      <link>https://dev.to/swiftnodes/dedicated-node-vs-shared-rpc-endpoint-when-you-actually-need-one-2026</link>
      <guid>https://dev.to/swiftnodes/dedicated-node-vs-shared-rpc-endpoint-when-you-actually-need-one-2026</guid>
      <description>&lt;p&gt;"Dedicated  node" is one of the most common things people search for when their RPC starts misbehaving. Usually the real question underneath is: &lt;em&gt;my shared endpoint is rate-limiting me, returning inconsistent answers, or missing a method — do I need my own node?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Sometimes the answer is yes. More often it is "not yet", and the fix costs a fraction of a dedicated node. This post covers what each option actually is, what dedicated nodes cost in 2026 according to the providers' own pages, and the concrete signals that tell you which one you need.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you are buying in each case
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A shared endpoint&lt;/strong&gt; is a URL in front of a pool of nodes that many customers use. The provider load-balances your requests across that pool, and your plan buys a rate limit (requests per second) and sometimes a monthly volume, not a machine. SwiftNodes is a shared-endpoint provider: every plan is a rate limit across all 86 chains we serve, from 2 requests per second on the free tier to 500 on Pro.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A dedicated node&lt;/strong&gt; is a node, or a small cluster, that serves only you. You buy the machine's capacity. There is no per-request limit beyond what the hardware can do, and you usually get a say in the configuration: archive or full, which client, which tracing flags, which region.&lt;/p&gt;

&lt;p&gt;The difference that matters most is not speed. It is &lt;strong&gt;consistency&lt;/strong&gt;. A shared endpoint gives you whichever healthy node answers. A dedicated node always gives you the same one.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a shared pool looks like from the client side
&lt;/h2&gt;

&lt;p&gt;This is easy to state and hard to picture, so here is a measurement from our own endpoint. On 2026-09-23 I sent the same kinds of requests to our Cronos endpoint and got two different answers depending on which upstream node served them:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Request&lt;/th&gt;
&lt;th&gt;Upstream A&lt;/th&gt;
&lt;th&gt;Upstream B&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Largest &lt;code&gt;eth_getLogs&lt;/code&gt; range&lt;/td&gt;
&lt;td&gt;10,000 blocks (&lt;code&gt;maximum [from, to] blocks distance: 10000&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;50,000 blocks (&lt;code&gt;exceed maximum block range: 50000&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oldest available height&lt;/td&gt;
&lt;td&gt;&lt;code&gt;lowest height is 90850001&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;lowest height is 95187533&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Both nodes were healthy. They were simply configured differently, and a pooled endpoint exposes the union of their quirks. For most applications this is invisible: you read the head, send transactions, and query recent logs. For an indexer backfilling months of history, it is the difference between a job that finishes and one that fails every few thousand requests on whichever node has the tighter limit.&lt;/p&gt;

&lt;p&gt;The shared-endpoint fix is to &lt;strong&gt;code to the strictest node&lt;/strong&gt;: chunk &lt;code&gt;eth_getLogs&lt;/code&gt; at the smaller cap, and assume the shallower history. That is what our &lt;a href="https://swiftnodes.io/blog/cronos-rpc-developer-guide" rel="noopener noreferrer"&gt;Cronos guide&lt;/a&gt; recommends. The dedicated-node fix is to own the configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  What dedicated nodes cost in 2026
&lt;/h2&gt;

&lt;p&gt;Pricing for dedicated infrastructure is mostly not public. What the providers themselves publish, as of September 2026:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Provider&lt;/th&gt;
&lt;th&gt;Dedicated pricing, as published&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;QuickNode&lt;/td&gt;
&lt;td&gt;Dedicated clusters are part of Enterprise: "Custom pricing" (pricing page)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GetBlock&lt;/td&gt;
&lt;td&gt;"Public dedicated pricing: Yes, from $1,000" (pricing page)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chainstack&lt;/td&gt;
&lt;td&gt;$199/mo plan + compute from $0.50/hour + storage. Their own examples: Ethereum full node ~$1,279/mo, Zora full ~$445/mo, Celo full ~$767/mo (Chainstack, 2026-09-02)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So a dedicated Ethereum node lands at roughly &lt;strong&gt;$1,000–1,300 a month&lt;/strong&gt; where a price is published, and more when you need archive or several regions. Compare that with shared flat-rate plans, which run from $49 to a few hundred dollars a month. On SwiftNodes, Pro is $249/mo for 500 requests per second on every chain.&lt;/p&gt;

&lt;p&gt;That gap is the whole decision. A dedicated node needs to fix a problem worth about $1,000 a month to you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four signals that you actually need one
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. You need a method or configuration the shared pool does not offer.&lt;/strong&gt; Examples: &lt;code&gt;debug_traceTransaction&lt;/code&gt; on a chain where the pool has no tracing nodes (our Cronos endpoint returns &lt;code&gt;-32601&lt;/code&gt; for the whole &lt;code&gt;debug_&lt;/code&gt; namespace), full archive state on a chain where the shared nodes prune (Cronos keeps about 100 blocks of state), or a specific client for its custom namespace. Check the provider's method support first. Ours is on the &lt;a href="https://swiftnodes.io/docs/method-support" rel="noopener noreferrer"&gt;measured method matrix&lt;/a&gt;. If the method is not there on any shared plan, a dedicated node is the honest answer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Your sustained load is above the top shared plan.&lt;/strong&gt; Look at your &lt;em&gt;sustained&lt;/em&gt; requests per second, not your peak. If you consistently need several hundred requests per second on one chain, the per-request economics favour your own capacity. Bursts are cheaper to absorb with batching and retries — see &lt;a href="https://swiftnodes.io/blog/json-rpc-batching" rel="noopener noreferrer"&gt;JSON-RPC batching&lt;/a&gt; and &lt;a href="https://swiftnodes.io/blog/rpc-rate-limits" rel="noopener noreferrer"&gt;RPC rate limits&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. You need every response to come from the same node.&lt;/strong&gt; Backfilling indexers, reconciliation jobs and anything that compares state across consecutive calls suffer when consecutive requests can land on nodes a block apart, or with different pruning. You can engineer around it (pin block numbers in every call, use the smallest shared limits), but past a certain volume a single node is simpler.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Compliance or data locality says so.&lt;/strong&gt; Some teams must know exactly which machine in which jurisdiction served a request. A shared pool cannot promise that. A dedicated node in a named region can.&lt;/p&gt;

&lt;p&gt;If none of these apply, you probably do not need a dedicated node yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signals that look like "I need dedicated" but are not
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;429 errors on a free or public endpoint.&lt;/strong&gt; That is a rate limit, not a capacity problem. Past 2 requests per second, our free tier answers &lt;code&gt;{"error":"Rate limited","limit":2,"plan":"Free"}&lt;/code&gt;. A paid shared plan moves that limit from 2 to 50–500 requests per second for $49–249 a month. See &lt;a href="https://swiftnodes.io/blog/public-rpc-vs-paid-rpc" rel="noopener noreferrer"&gt;public RPC vs paid RPC&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Occasional stale or lagging responses.&lt;/strong&gt; Usually one lagging node in a pool the provider should be ejecting. Our health checker compares every upstream's height with the chain's best and stops routing to one that trails by more than 50 blocks (EVM) for two consecutive rounds. &lt;a href="https://swiftnodes.io/blog/is-your-rpc-node-synced-stale-endpoint" rel="noopener noreferrer"&gt;Is your RPC node synced?&lt;/a&gt; covers how to detect it from the client side.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"My &lt;code&gt;eth_getLogs&lt;/code&gt; calls fail."&lt;/strong&gt; Almost always a range cap. Chunk the range; a dedicated node would have a cap too, just one you set yourself. See &lt;a href="https://swiftnodes.io/blog/eth-getlogs-range-caps" rel="noopener noreferrer"&gt;eth_getLogs range caps&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Latency.&lt;/strong&gt; A dedicated node in the wrong region is slower than a shared pool with a node near you. Measure p95 from where your code actually runs before paying for isolation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The middle option: run your own node
&lt;/h2&gt;

&lt;p&gt;If you need consistency and configuration control but not a vendor-managed machine, running a node yourself is the third choice. The hardware is often cheaper than a managed dedicated node. The operations work (upgrades, disk growth, resyncs, monitoring) is not. We did the full cost comparison in &lt;a href="https://swiftnodes.io/blog/self-hosted-node-vs-rpc-provider" rel="noopener noreferrer"&gt;self-hosted node vs RPC provider&lt;/a&gt;, and the Ethereum archive numbers are in &lt;a href="https://swiftnodes.io/blog/ethereum-archive-node-cost" rel="noopener noreferrer"&gt;Ethereum archive node cost&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A common pattern that works well: &lt;strong&gt;shared endpoint for everything, plus one self-run or dedicated node for the single job that needs consistency&lt;/strong&gt; (usually the indexer backfill). You pay dedicated prices for one chain and one workload, not for your whole stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;A dedicated node buys you consistency and configuration control, not raw speed. Published 2026 prices put a dedicated Ethereum node at roughly $1,000–1,300 a month (GetBlock from $1,000, Chainstack ~$1,279; QuickNode and others quote privately). Shared flat-rate plans cost $49–249 a month. You need dedicated when a method or history depth is missing from every shared plan, when sustained load exceeds the top shared tier, when consecutive calls must hit the same node, or when compliance requires a named machine. Rate limits, stale nodes, &lt;code&gt;eth_getLogs&lt;/code&gt; errors and latency are usually fixed more cheaply without one.&lt;/p&gt;

&lt;p&gt;If a shared endpoint covers your case, SwiftNodes serves 86 chains on one key with flat monthly pricing and a free tier at 2 requests per second, enough to test your workload before you decide anything. &lt;a href="https://swiftnodes.io/" rel="noopener noreferrer"&gt;Get an API key&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://swiftnodes.io/blog/dedicated-node-vs-shared-rpc" rel="noopener noreferrer"&gt;SwiftNodes blog&lt;/a&gt;. SwiftNodes provides flat-rate multi-chain RPC endpoints — HTTP + WebSocket, 75+ chains, no per-request metering. &lt;a href="https://swiftnodes.io/" rel="noopener noreferrer"&gt;Grab a free key&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ethereum</category>
      <category>blockchain</category>
      <category>web3</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Cronos RPC: A Developer's Guide</title>
      <dc:creator>SwiftNodes</dc:creator>
      <pubDate>Thu, 24 Sep 2026 05:17:41 +0000</pubDate>
      <link>https://dev.to/swiftnodes/cronos-rpc-a-developers-guide-5855</link>
      <guid>https://dev.to/swiftnodes/cronos-rpc-a-developers-guide-5855</guid>
      <description>&lt;p&gt;Cronos is Crypto.com's EVM chain: Solidity contracts, &lt;code&gt;eth_*&lt;/code&gt; methods, MetaMask, viem and ethers all work unchanged. Underneath, it is a Cosmos SDK chain with an EVM module, and that shows up in exactly the places where Ethereum habits break: how fast blocks arrive, how much history a node keeps, and what the error messages look like when you ask for something it has already thrown away.&lt;/p&gt;

&lt;p&gt;Plenty of documentation still describes Cronos as a 5–6 second chain. Our own chain page said so until this post. The chain I measured today produces a block roughly every &lt;strong&gt;0.42 seconds&lt;/strong&gt;, which changes how you should poll, index and cache.&lt;/p&gt;

&lt;p&gt;Everything below was measured on 2026-09-23 through &lt;code&gt;https://rpc.swiftnodes.io/rpc/cronos&lt;/code&gt;. Re-run any of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The essentials
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Chain ID&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;25&lt;/code&gt; (&lt;code&gt;0x19&lt;/code&gt;); &lt;code&gt;net_version&lt;/code&gt; also returns &lt;code&gt;25&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Native token&lt;/td&gt;
&lt;td&gt;CRO, 18 decimals&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Block time&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;~0.42 s&lt;/strong&gt;: 0.410 s over 100 blocks, 0.422 over 1,000, 0.419 over 10,000, 0.421 over 100,000, 0.428 over 1,000,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Head at time of writing&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;95,687,161&lt;/code&gt; (2026-09-23 14:03:29 UTC)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Finality&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;latest&lt;/code&gt; = &lt;code&gt;safe&lt;/code&gt; = &lt;code&gt;finalized&lt;/code&gt;, in every batched read&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Block gas limit&lt;/td&gt;
&lt;td&gt;60,000,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Base fee&lt;/td&gt;
&lt;td&gt;375 gwei, flat across the last 11 blocks; &lt;code&gt;eth_maxPriorityFeePerGas&lt;/code&gt; 3.75 gwei&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;eth_getLogs&lt;/code&gt; range&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;10,000 blocks&lt;/strong&gt; (about 70 minutes of chain time), and an address is required&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Historical state&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;~100 blocks&lt;/strong&gt; (under a minute)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;debug_*&lt;/code&gt; / &lt;code&gt;trace_*&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Not available (&lt;code&gt;-32601&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WebSocket&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;eth_subscribe("newHeads")&lt;/code&gt; works&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RPC&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://rpc.swiftnodes.io/rpc/cronos?key=YOUR_API_KEY&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Connecting
&lt;/h2&gt;

&lt;p&gt;Any EVM client works. viem does not need a custom chain definition, because it ships &lt;code&gt;cronos&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createPublicClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;webSocket&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;viem&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;cronos&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;viem/chains&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createPublicClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cronos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;http&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://rpc.swiftnodes.io/rpc/cronos?key=YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getChainId&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;      &lt;span class="c1"&gt;// 25&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBlockNumber&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;  &lt;span class="c1"&gt;// 95687161n at time of writing&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://rpc.swiftnodes.io/rpc/cronos?key=YOUR_API_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The two token contracts most integrations touch first, both read back with &lt;code&gt;eth_call&lt;/code&gt; today:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Token&lt;/th&gt;
&lt;th&gt;Address&lt;/th&gt;
&lt;th&gt;&lt;code&gt;decimals()&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;WCRO&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0x5C7F8A570d578ED84E63fdFA7b1eE72dEae1AE23&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;USDC&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0xc21223249CA28397B4B6541dfFaEcC539BfF0c59&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;6&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;USDC's 6 decimals is the usual trap for code that assumes 18 on an EVM chain.&lt;/p&gt;

&lt;h2&gt;
  
  
  0.42-second blocks change your polling math
&lt;/h2&gt;

&lt;p&gt;At ~0.42 s per block, Cronos produces roughly &lt;strong&gt;205,000 blocks per day&lt;/strong&gt;. A million blocks is under five days. Three consequences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Polling &lt;code&gt;eth_blockNumber&lt;/code&gt; every second skips blocks.&lt;/strong&gt; Each poll sees the head move by two or three. That's fine if you only need the tip. If you process every block, fetch ranges (&lt;code&gt;fromBlock&lt;/code&gt; → &lt;code&gt;toBlock&lt;/code&gt;), not "the next one".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Block-count thresholds from Ethereum are far too short here.&lt;/strong&gt; "Wait 12 blocks" is five seconds on Cronos. If you express staleness, cache TTLs or confirmation UX in blocks, convert them from seconds first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscriptions beat polling.&lt;/strong&gt; Over WebSocket, &lt;code&gt;newHeads&lt;/code&gt; delivered 10 consecutive heads in 4.4 seconds after subscribing, the first one 675 ms in:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createPublicClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cronos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;webSocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;wss://rpc.swiftnodes.io/ws/cronos?key=YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;watchBlocks&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;onBlock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transactions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Handle reconnects without losing events. At this block rate, a 30-second disconnect is about 70 blocks. The pattern is in &lt;a href="https://swiftnodes.io/blog/websocket-reconnect-without-losing-events" rel="noopener noreferrer"&gt;WebSocket reconnects without losing events&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finality: there is nothing to wait for
&lt;/h2&gt;

&lt;p&gt;Cronos uses CometBFT-style consensus, so a committed block is final. I read &lt;code&gt;latest&lt;/code&gt;, &lt;code&gt;safe&lt;/code&gt; and &lt;code&gt;finalized&lt;/code&gt; in a single JSON-RPC batch three times, so all three came from the same instant. They returned the same height every time (&lt;code&gt;95,687,363&lt;/code&gt;, then &lt;code&gt;95,687,365&lt;/code&gt;, then &lt;code&gt;95,687,367&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;You can drop reorg handling written for probabilistic chains. Keep a sanity check anyway — the reasoning is in &lt;a href="https://swiftnodes.io/blog/handling-chain-reorgs-indexer" rel="noopener noreferrer"&gt;handling chain reorgs in an indexer&lt;/a&gt; — but you do not need a confirmation depth.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gas: flat base fee, near-empty blocks
&lt;/h2&gt;

&lt;p&gt;Over the last 10 blocks, &lt;code&gt;eth_feeHistory&lt;/code&gt; returned a base fee of exactly &lt;strong&gt;375 gwei&lt;/strong&gt; for every block. The highest &lt;code&gt;gasUsedRatio&lt;/code&gt; was 0.0083 (0.83% of the 60M gas limit), and several blocks were empty. &lt;code&gt;eth_gasPrice&lt;/code&gt; returned 378.75 gwei, which is the base fee plus the 3.75 gwei suggested tip.&lt;/p&gt;

&lt;p&gt;Blocks are this empty, so the tip is not buying you anything. A real type-2 transaction I pulled from the chain paid an effective gas price of &lt;code&gt;375,000,000,001&lt;/code&gt; wei: the base fee plus a 1-wei tip. Basic cost figures:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Gas (measured)&lt;/th&gt;
&lt;th&gt;Cost at 375 gwei&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Plain CRO transfer&lt;/td&gt;
&lt;td&gt;21,000 (&lt;code&gt;eth_estimateGas&lt;/code&gt; → &lt;code&gt;0x5208&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;0.007875 CRO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sample contract call (from chain)&lt;/td&gt;
&lt;td&gt;127,908&lt;/td&gt;
&lt;td&gt;~0.048 CRO&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you hard-code gas prices from Ethereum-mainnet habits (a few gwei), your transactions will never be included, because the base fee here is 375 gwei. For the general method, see &lt;a href="https://swiftnodes.io/blog/estimating-gas-eth-estimategas-eip-1559" rel="noopener noreferrer"&gt;estimating gas with eth_estimateGas and EIP-1559&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  eth_getLogs: 10,000 blocks, address required
&lt;/h2&gt;

&lt;p&gt;I queried USDC &lt;code&gt;Transfer&lt;/code&gt; events over growing ranges:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Range (blocks)&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;4 events, 158 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1,000&lt;/td&gt;
&lt;td&gt;41 events, 359 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5,000&lt;/td&gt;
&lt;td&gt;217 events, 369 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10,000&lt;/td&gt;
&lt;td&gt;413 events, 1,016 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;50,000&lt;/td&gt;
&lt;td&gt;&lt;code&gt;maximum [from, to] blocks distance: 10000&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;100,000&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-32701 exceed maximum block range: 50000&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two different limits came back from two different upstream nodes. Code to the smaller one: &lt;strong&gt;10,000 blocks&lt;/strong&gt;. On Cronos, that covers about 70 minutes, so backfilling a single day takes about 21 requests and a month about 620. The chunking loop from &lt;a href="https://swiftnodes.io/blog/eth-getlogs-range-caps" rel="noopener noreferrer"&gt;eth_getLogs range caps&lt;/a&gt; applies, with one change: chunk size in blocks has to be sized for a fast chain.&lt;/p&gt;

&lt;p&gt;A query with &lt;strong&gt;only a topic and no &lt;code&gt;address&lt;/code&gt;&lt;/strong&gt; was refused three times out of three with &lt;code&gt;-32701&lt;/code&gt; and the message "Please specify an address in your request". Wide event scans ("every ERC-20 Transfer on the chain") are not something this endpoint will serve. Filter by contract.&lt;/p&gt;

&lt;h2&gt;
  
  
  History: plan for a node that forgets
&lt;/h2&gt;

&lt;p&gt;This is the part that will break an Ethereum-shaped indexer. The Cosmos SDK nodes behind this endpoint prune aggressively.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;State (balances, &lt;code&gt;eth_call&lt;/code&gt; at a past block) lasts about 100 blocks.&lt;/strong&gt; &lt;code&gt;eth_getBalance&lt;/code&gt; at 20 and 50 blocks behind the head succeeded. At about 100 blocks behind, and at every depth from there out to 3,000,000, it failed with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;codespace sdk code 18: invalid request: failed to load state at height 91687369;
version mismatch on immutable IAVL tree; version does not exist.
Version has either been pruned, or is for a future block height
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A hundred blocks is about 40 seconds. "Read the balance as of this morning" is not a query this endpoint can answer. If you need historical state, you derive it from events, or you run an archive node. We explain that trade-off in &lt;a href="https://swiftnodes.io/blog/full-node-vs-archive-node" rel="noopener noreferrer"&gt;full node vs archive node&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Blocks and logs go back further, but the floor depends on which node answers.&lt;/strong&gt; One upstream reported its lowest height as &lt;code&gt;90,850,001&lt;/code&gt;, and block 90,850,001 (2026-08-30) came back intact. Another reported &lt;code&gt;95,187,533&lt;/code&gt;, about 500,000 blocks or ~2.4 days behind the head. Design backfills around the shorter window, and treat "not available" as a per-node answer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pre-floor blocks return &lt;code&gt;null&lt;/code&gt;, not an error.&lt;/strong&gt; &lt;code&gt;eth_getBlockByNumber&lt;/code&gt; for block 1, 1,000,000 and 50,000,000 returned &lt;code&gt;result: null&lt;/code&gt;, and so did the &lt;code&gt;earliest&lt;/code&gt; tag. Code that treats &lt;code&gt;null&lt;/code&gt; as "block does not exist yet" will misreport pruned history as the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  What works and what doesn't
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;eth_call&lt;/code&gt;, &lt;code&gt;eth_estimateGas&lt;/code&gt;, &lt;code&gt;eth_feeHistory&lt;/code&gt;, &lt;code&gt;eth_maxPriorityFeePerGas&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Works&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;eth_getBlockReceipts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Works (all receipts for a block in one call)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;eth_call&lt;/code&gt; with a state-override object&lt;/td&gt;
&lt;td&gt;Works (see &lt;a href="https://swiftnodes.io/blog/eth-call-state-overrides" rel="noopener noreferrer"&gt;state overrides&lt;/a&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;txpool_status&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Works (&lt;code&gt;pending&lt;/code&gt; / &lt;code&gt;queued&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;eth_createAccessList&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Works &lt;strong&gt;only with &lt;code&gt;gas&lt;/code&gt; set&lt;/strong&gt;; without it: &lt;code&gt;gas must be set when using authorization list&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;debug_traceTransaction&lt;/code&gt;, &lt;code&gt;debug_traceBlockByNumber&lt;/code&gt;, &lt;code&gt;trace_transaction&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Not available: &lt;code&gt;-32601 method does not exist/is not available&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;eth_subscribe("newHeads")&lt;/code&gt; over &lt;code&gt;wss://&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Works&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;eth_createAccessList&lt;/code&gt; error is misleading: my call had no authorization list, and passing any &lt;code&gt;gas&lt;/code&gt; value (I used 200,000) made it succeed. If your library calls it without gas, add one.&lt;/p&gt;

&lt;p&gt;No tracing means no call trees for debugging reverts or tracking internal CRO transfers over RPC. To find out where a transaction failed, re-run it with &lt;code&gt;eth_call&lt;/code&gt; at the latest block before state moves on — which, at ~100 blocks of state, is less than a minute.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means for your stack
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Think in seconds, not blocks.&lt;/strong&gt; Convert every Ethereum-tuned block count (confirmations, TTLs, backoff) to wall-clock time first. At 0.42 s, ported block numbers are about 30x too short.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chunk &lt;code&gt;eth_getLogs&lt;/code&gt; at 10,000 blocks and always pass an address.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Read state at the head, and derive history from events.&lt;/strong&gt; A shared endpoint keeps about 40 seconds of state.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Handle &lt;code&gt;null&lt;/code&gt; for pruned blocks&lt;/strong&gt; and expect the floor to change with the node that answers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Skip confirmation depth, keep a sanity check.&lt;/strong&gt; Finality is single-block.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;Cronos is EVM chain &lt;code&gt;25&lt;/code&gt; with &lt;strong&gt;~0.42-second blocks&lt;/strong&gt; (stable from 100 to 1,000,000-block windows) and single-block finality (&lt;code&gt;latest&lt;/code&gt; = &lt;code&gt;safe&lt;/code&gt; = &lt;code&gt;finalized&lt;/code&gt;). The base fee sits at a flat &lt;strong&gt;375 gwei&lt;/strong&gt; with blocks under 1% full. &lt;code&gt;eth_getLogs&lt;/code&gt; is capped at &lt;strong&gt;10,000 blocks&lt;/strong&gt; (about 70 minutes) and needs an address. Historical state survives about &lt;strong&gt;100 blocks&lt;/strong&gt;, block history between ~2.4 and ~24 days depending on the node, and pruned blocks come back as &lt;code&gt;null&lt;/code&gt;. There is no &lt;code&gt;debug_&lt;/code&gt;/&lt;code&gt;trace_&lt;/code&gt; namespace, &lt;code&gt;eth_createAccessList&lt;/code&gt; needs &lt;code&gt;gas&lt;/code&gt;, and &lt;code&gt;newHeads&lt;/code&gt; subscriptions deliver every block.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://rpc.swiftnodes.io/rpc/cronos?key=YOUR_API_KEY
wss://rpc.swiftnodes.io/ws/cronos?key=YOUR_API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://swiftnodes.io/cronos-rpc" rel="noopener noreferrer"&gt;Cronos RPC page&lt;/a&gt; shows the live status of these endpoints. One key covers all 86 chains, and the free tier is 2 requests per second — enough to re-run every probe in this post.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://swiftnodes.io/blog/cronos-rpc-developer-guide" rel="noopener noreferrer"&gt;SwiftNodes blog&lt;/a&gt;. SwiftNodes provides flat-rate multi-chain RPC endpoints — HTTP + WebSocket, 75+ chains, no per-request metering. &lt;a href="https://swiftnodes.io/" rel="noopener noreferrer"&gt;Grab a free key&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cronos</category>
      <category>blockchain</category>
      <category>web3</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Babylon RPC: A Developer's Guide (bbn-1, 9.6s Blocks, and a Pruning Floor That Moves)</title>
      <dc:creator>SwiftNodes</dc:creator>
      <pubDate>Tue, 22 Sep 2026 06:04:23 +0000</pubDate>
      <link>https://dev.to/swiftnodes/babylon-rpc-a-developers-guide-bbn-1-96s-blocks-and-a-pruning-floor-that-moves-161n</link>
      <guid>https://dev.to/swiftnodes/babylon-rpc-a-developers-guide-bbn-1-96s-blocks-and-a-pruning-floor-that-moves-161n</guid>
      <description>&lt;p&gt;Babylon is the chain where Bitcoin holders stake BTC natively — no bridge, no wrapper — to secure proof-of-stake networks, and it is not an EVM chain: no &lt;code&gt;eth_*&lt;/code&gt; methods, no numeric chain ID, and a REST/gRPC surface that matters as much as the RPC one. Most integration pain here comes from expecting the EVM habits, and from one thing that genuinely surprised me when I measured it: the pruning floor is not a single number.&lt;/p&gt;

&lt;p&gt;Everything below was measured on 2026-09-22 through &lt;code&gt;https://rpc.swiftnodes.io/rpc/babylon&lt;/code&gt; and the public LCD, from inside a datacenter so the latency figures are not a residential round trip. Re-run any of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The essentials
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Network / chain ID&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;bbn-1&lt;/code&gt; (a string; there is no numeric chain ID)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consensus client&lt;/td&gt;
&lt;td&gt;CometBFT &lt;code&gt;0.38.22&lt;/code&gt; (protocol p2p 8, block 11, app 0)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Block time&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;~9.58 s&lt;/strong&gt; (stable over 100, 1,000 and 5,000-block windows)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Head at time of writing&lt;/td&gt;
&lt;td&gt;&lt;code&gt;4,580,805&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Active validators&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;68&lt;/strong&gt;, of &lt;code&gt;max_validators: 100&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unbonding period&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1,814,400 s = 21 days&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Block limits&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;max_bytes&lt;/code&gt; 22,020,096 (~21 MiB), &lt;code&gt;max_gas&lt;/code&gt; 300,000,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EVM methods&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;None.&lt;/strong&gt; &lt;code&gt;eth_chainId&lt;/code&gt;, &lt;code&gt;eth_blockNumber&lt;/code&gt;, &lt;code&gt;eth_getLogs&lt;/code&gt;, &lt;code&gt;web3_clientVersion&lt;/code&gt; → &lt;code&gt;-32601 Method not found&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RPC&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://rpc.swiftnodes.io/rpc/babylon?key=YOUR_API_KEY&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Also exposed&lt;/td&gt;
&lt;td&gt;gRPC and REST/LCD — for Babylon these are not optional extras&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Connecting
&lt;/h2&gt;

&lt;p&gt;JSON-RPC 2.0 over POST, method in the body, and note the &lt;strong&gt;object&lt;/strong&gt; params (&lt;code&gt;{"height": "..."}&lt;/code&gt;), not Ethereum's array.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://rpc.swiftnodes.io/rpc/babylon?key&lt;span class="o"&gt;=&lt;/span&gt;YOUR_API_KEY &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"jsonrpc":"2.0","id":1,"method":"status","params":{}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;RPC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://rpc.swiftnodes.io/rpc/babylon?key=YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;RPC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;content-type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;jsonrpc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sync_info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;latest_block_height&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// 4,580,805 at time of writing&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;block&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;block&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chain_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;                  &lt;span class="c1"&gt;// "bbn-1"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Heights are &lt;strong&gt;strings&lt;/strong&gt;. &lt;code&gt;{"height": 4580805}&lt;/code&gt; as a JSON number is rejected, which is the single most common reason a working Ethereum client breaks on a Cosmos chain.&lt;/p&gt;

&lt;p&gt;CometBFT's own REST-style paths (&lt;code&gt;GET /status&lt;/code&gt;, &lt;code&gt;/block?height=N&lt;/code&gt;) do &lt;strong&gt;not&lt;/strong&gt; work through a load-balanced RPC path — POST only. And errors arrive as a JSON-RPC &lt;code&gt;error&lt;/code&gt; object with &lt;strong&gt;HTTP 200&lt;/strong&gt;, so a client that only checks the status code will read "no data" instead of "refused".&lt;/p&gt;

&lt;h2&gt;
  
  
  The 9.6 second block is the design, not a problem
&lt;/h2&gt;

&lt;p&gt;I checked this three ways because a single window has misled me before on a Cosmos chain:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Window&lt;/th&gt;
&lt;th&gt;Measured&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;last 100 blocks&lt;/td&gt;
&lt;td&gt;9.62 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;last 1,000 blocks&lt;/td&gt;
&lt;td&gt;9.57 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;last 5,000 blocks&lt;/td&gt;
&lt;td&gt;9.58 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;whole life (4,580,805 blocks since the April 2025 launch)&lt;/td&gt;
&lt;td&gt;~10.0 s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Consistent. Babylon is a slow-block chain on purpose — it is coordinating Bitcoin-signature verification, not racing for throughput. Two consequences worth designing for: &lt;strong&gt;do not&lt;/strong&gt; use "blocks since X" as a proxy for time, and &lt;strong&gt;do not&lt;/strong&gt; copy a 1-second-chain timeout budget. Anything waiting on a Babylon block should budget tens of seconds, and a &lt;code&gt;max_age_num_blocks&lt;/code&gt; of 100,000 is roughly 11.3 days of chain history, not a few hours.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is actually in a Babylon block
&lt;/h2&gt;

&lt;p&gt;The block I sampled at &lt;code&gt;4,580,800&lt;/code&gt; held 23 transactions, and decoding them gave one message type: &lt;code&gt;MsgAddFinalitySig&lt;/code&gt;. That is the chain working — finality providers attesting to the state of consumer chains. Two things follow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Block "activity" on Babylon is mostly protocol traffic. Counting transactions per block tells you almost nothing about user demand here.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;block_results.txs_results&lt;/code&gt; is where the useful per-transaction detail lives: each of the 23 txs I sampled reported ordinary Cosmos gas (&lt;code&gt;gas_wanted&lt;/code&gt; 126,916 / &lt;code&gt;gas_used&lt;/code&gt; 116,127) and &lt;strong&gt;12 events each&lt;/strong&gt;. And those per-tx events are easy to miss, because the &lt;em&gt;top-level&lt;/em&gt; &lt;code&gt;events&lt;/code&gt; array on the same response was empty, as were &lt;code&gt;begin_block&lt;/code&gt;/&lt;code&gt;end_block&lt;/code&gt; — on this chain the events hang off individual results.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Which is the practical version of the same rule: &lt;code&gt;block&lt;/code&gt; and &lt;code&gt;block_results&lt;/code&gt; are &lt;strong&gt;two separate calls&lt;/strong&gt;. A poller that fetches only &lt;code&gt;block&lt;/code&gt; sees 23 transactions and zero events, and concludes — wrongly — that nothing happened.&lt;/p&gt;

&lt;p&gt;Also, like every CometBFT 0.38 chain I have measured recently: the header contains &lt;strong&gt;no &lt;code&gt;num_txs&lt;/code&gt; and no &lt;code&gt;total_txs&lt;/code&gt;&lt;/strong&gt; — 0.38 removed them. Read &lt;code&gt;block.data.txs.length&lt;/code&gt;. Code ported from 0.37 gets &lt;code&gt;undefined&lt;/code&gt;, which becomes &lt;code&gt;NaN&lt;/code&gt; in arithmetic and a silently-empty loop in anything that filters "skip empty blocks".&lt;/p&gt;

&lt;h2&gt;
  
  
  The pruning floor gave three different answers in six calls
&lt;/h2&gt;

&lt;p&gt;This is the finding to plan around. Requesting &lt;code&gt;block&lt;/code&gt; at height 1 six times, alternating between the pool's upstream nodes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;height 1        -&amp;gt; lowest height is 2694820
height 1        -&amp;gt; lowest height is 3017244
height 1000     -&amp;gt; lowest height is 2694820
height 1000     -&amp;gt; lowest height is 2556426
height 1000000  -&amp;gt; lowest height is 3017244
height 1000000  -&amp;gt; lowest height is 2694820
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Three distinct floors — 2,556,426, 2,694,820 and 3,017,244 — for the same URL.&lt;/strong&gt; Each is a different node in the pool, each pruning on its own schedule. Heights at and above ~2.6M resolved fine (2,600,000 → 2026-02-03, 4,000,000 → 2026-07-18); anything older fails with an explicit refusal, which is at least the honest failure mode.&lt;/p&gt;

&lt;p&gt;So:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"How far back can I read?" has no stable answer on a pooled RPC. Ask the node at startup — request height 1 and read the error — and treat the &lt;em&gt;highest&lt;/em&gt; number you see as the safe floor.&lt;/li&gt;
&lt;li&gt;Never cache a historical query's success as proof of retention. One success means one node had it.&lt;/li&gt;
&lt;li&gt;Babylon is not addressable as an archive target here (&lt;code&gt;chain_id&lt;/code&gt; is null in our own config and no &lt;code&gt;archive&lt;/code&gt; flag is set on the HTTP rows), so plan around the ~2.5M+ window, not from genesis. If you need genesis-to-now history, you need an indexer or an archival node you run yourself.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;tx_search&lt;/code&gt; refuses to search
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Query&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tx.height=4580802&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;113 ms, &lt;code&gt;total_count=23&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tx.height&amp;gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;16,095 ms, &lt;code&gt;Backend node error&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;message.action='/cosmos.bank.v1beta1.MsgSend'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;28,082 ms, &lt;code&gt;Backend node error&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tm.event='Tx'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;accepted, &lt;code&gt;total_count=0&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Exact-height lookup is fast. Ranges and attribute queries burn 16–28 seconds and then error rather than returning a partial answer — the indexer here is not built for them. And &lt;code&gt;tm.event='Tx'&lt;/code&gt; is a &lt;em&gt;websocket&lt;/em&gt; event query, so through plain HTTP RPC it returns an empty set rather than an error, which is easy to misread as "no transactions match".&lt;/p&gt;

&lt;p&gt;If you need "transactions matching X over a period", iterate the heights you care about, or use the LCD's module endpoints. Treat &lt;code&gt;tx_search&lt;/code&gt; as a by-height lookup, not a search index.&lt;/p&gt;

&lt;h2&gt;
  
  
  The LCD is half the API
&lt;/h2&gt;

&lt;p&gt;The RPC gives you blocks and consensus. Babylon's actual domain — BTC staking — is on REST/gRPC, and it answers fast:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Covenant keys, slashing params, staking thresholds — the BTC-staking rulebook&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://babylon-rest.publicnode.com/babylon/btcstaking/v1/params

&lt;span class="c"&gt;# Unbonding time (21 days) and the 100-validator cap&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://babylon-rest.publicnode.com/cosmos/staking/v1beta1/params

&lt;span class="c"&gt;# Total supply of the base denom; BABY has 6 decimals, so 1 BABY = 1,000,000 ubbn&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s1"&gt;'https://babylon-rest.publicnode.com/cosmos/bank/v1beta1/supply/by_denom?denom=ubbn'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All three returned HTTP 200 in my pass, including the &lt;code&gt;babylon/btcstaking/v1/params&lt;/code&gt; path with the covenant public keys that make Babylon's slashing scripts work. If you are building on Babylon staking state, the LCD is the right tool and the RPC is the wrong one — and SwiftNodes carries REST rows alongside RPC for this chain for that reason.&lt;/p&gt;

&lt;h2&gt;
  
  
  Websocket: what I could and could not verify
&lt;/h2&gt;

&lt;p&gt;I want to be precise here rather than repeat a field in a status page. &lt;code&gt;wss://babylon-rpc.publicnode.com/websocket&lt;/code&gt; completed the handshake, answered &lt;code&gt;health&lt;/code&gt;, and &lt;strong&gt;acknowledged&lt;/strong&gt; a &lt;code&gt;subscribe&lt;/code&gt; to &lt;code&gt;tm.event='NewBlockEvent'&lt;/code&gt; — and then delivered &lt;strong&gt;no event frames in 40 seconds&lt;/strong&gt; on a chain that produces a block every ~9.6 seconds, so it should have delivered roughly four. And through our own public path, a WebSocket upgrade on &lt;code&gt;/rpc/babylon&lt;/code&gt; currently comes back as a &lt;code&gt;301&lt;/code&gt; to a docs page rather than a &lt;code&gt;101&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So: treat Babylon subscriptions as unavailable until proven otherwise, and build on polling at ~10 s intervals. One 40-second sample is not proof of impossibility — but it is enough to stop me recommending a surface I could not get to emit an event.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means for your stack
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Budget for 9.6-second blocks&lt;/strong&gt; everywhere: timeouts, staleness thresholds, retry backoff, "how long until final" UX.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Never trust a single pruning measurement.&lt;/strong&gt; Ask for height 1, take the worst floor you observe, and expect it to differ per call.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Send heights as strings, read errors from the JSON body, and expect no &lt;code&gt;eth_*&lt;/code&gt; at all.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use &lt;code&gt;block_results&lt;/code&gt; for events and the LCD for staking state&lt;/strong&gt;, and treat &lt;code&gt;tx_search&lt;/code&gt; as by-height only.&lt;/p&gt;

&lt;p&gt;And the reason this is worth reading rather than guessing: every number above came from calling the endpoints, including the one that contradicted itself three times in six calls. SwiftNodes publishes Babylon RPC, gRPC and REST behind one key, and our weekly-measured &lt;a href="https://swiftnodes.io/docs/method-support" rel="noopener noreferrer"&gt;method support matrix&lt;/a&gt; exists because claims that are not measured go stale — Babylon's page is at &lt;a href="https://swiftnodes.io/babylon-rpc" rel="noopener noreferrer"&gt;babylon-rpc&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;Babylon Genesis is CometBFT 0.38.22 on chain &lt;code&gt;bbn-1&lt;/code&gt;: &lt;strong&gt;~9.58 s blocks&lt;/strong&gt; (confirmed across 100/1k/5k windows and the chain's own lifetime average), 68 active validators of a 100 cap, a &lt;strong&gt;21-day unbonding period&lt;/strong&gt;, ~21 MiB blocks, and no &lt;code&gt;eth_*&lt;/code&gt; surface whatsoever. Blocks are mostly &lt;code&gt;MsgAddFinalitySig&lt;/code&gt; traffic, so transaction counts are protocol noise rather than demand. &lt;code&gt;tx_search&lt;/code&gt; answers exact heights in ~113 ms and fails on ranges after 16–28 s. The header has no &lt;code&gt;num_txs&lt;/code&gt; in 0.38, &lt;code&gt;health&lt;/code&gt; returns an empty object, and the &lt;strong&gt;pruning floor is not one number&lt;/strong&gt; — six attempts named three different floors (2.55M, 2.69M, 3.02M), because each upstream node prunes on its own schedule. Use the LCD for the BTC-staking state, budget 10 seconds per block, and treat WebSockets as unproven.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;https://rpc.swiftnodes.io/rpc/babylon?key=YOUR_API_KEY
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One key covers all 86 chains — including the Cosmos-family ones whose surface is nothing like Ethereum's — and the free tier is 2 requests per second, enough to run every probe above and check the pruning floor yourself.&lt;/p&gt;

</description>
      <category>cosmos</category>
      <category>blockchain</category>
      <category>web3</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Hyperliquid RPC: A Developer's Guide (Chain 999, 1s Blocks, and a Block Number That Does Nothing)</title>
      <dc:creator>SwiftNodes</dc:creator>
      <pubDate>Mon, 21 Sep 2026 07:11:42 +0000</pubDate>
      <link>https://dev.to/swiftnodes/hyperliquid-rpc-a-developers-guide-chain-999-1s-blocks-and-a-block-number-that-does-nothing-l9h</link>
      <guid>https://dev.to/swiftnodes/hyperliquid-rpc-a-developers-guide-chain-999-1s-blocks-and-a-block-number-that-does-nothing-l9h</guid>
      <description>&lt;p&gt;Hyperliquid's EVM is a strange and interesting place to run a node: it finalises blocks about once a second, charges a base fee that never moves, and rejects &lt;code&gt;eth_maxPriorityFeePerGas&lt;/code&gt;-style market pricing entirely. But the thing that will actually cost you time is quieter. Ask this endpoint for state at a historical block and it will answer — cheerfully, with an empty error field — using the &lt;strong&gt;latest&lt;/strong&gt; state. There is no &lt;code&gt;archive&lt;/code&gt; fallback to save you, because the request never looks like it failed.&lt;/p&gt;

&lt;p&gt;Everything below was measured on 2026-09-21 through &lt;code&gt;https://rpc.swiftnodes.io/rpc/hyperliquid&lt;/code&gt;. Every claim here is a call you can re-run in a minute; the one that matters most has the exact recipe below.&lt;/p&gt;

&lt;h2&gt;
  
  
  The essentials
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Chain ID&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;999&lt;/code&gt; (&lt;code&gt;0x3e7&lt;/code&gt;) — &lt;code&gt;net_version&lt;/code&gt; agrees, &lt;code&gt;web3_clientVersion&lt;/code&gt; reports &lt;code&gt;hyperliquid evm Mainnet&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Block time&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;~0.985 s&lt;/strong&gt; (measured over 600 blocks)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Head at time of writing&lt;/td&gt;
&lt;td&gt;&lt;code&gt;46,479,662&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gas&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;baseFeePerGas&lt;/code&gt; of &lt;strong&gt;100,000,000 wei (0.1 gwei)&lt;/strong&gt; in every block sampled; &lt;code&gt;gasLimit&lt;/code&gt; 3,000,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;eth_maxPriorityFeePerGas&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns &lt;code&gt;0x0&lt;/code&gt; — there is no priority-fee market&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Block history&lt;/td&gt;
&lt;td&gt;~&lt;strong&gt;1.21M blocks (~14 days)&lt;/strong&gt;. Older heights error out&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State history&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Not honoured&lt;/strong&gt; — see the section below&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WebSocket&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;None.&lt;/strong&gt; &lt;code&gt;eth_subscribe&lt;/code&gt; → &lt;code&gt;-32601&lt;/code&gt;, no &lt;code&gt;rpc_ws&lt;/code&gt; on the route&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;eth_getLogs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Capped at 1,000 blocks &lt;em&gt;and&lt;/em&gt; a 1 MiB response&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tracing&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;debug_traceBlockByNumber&lt;/code&gt;, &lt;code&gt;trace_*&lt;/code&gt;, &lt;code&gt;txpool_*&lt;/code&gt; all &lt;code&gt;-32601&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://rpc.swiftnodes.io/rpc/hyperliquid?key=YOUR_API_KEY&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Connecting: the setup
&lt;/h2&gt;

&lt;p&gt;Standard EVM JSON-RPC, and standard clients work — with the caveats below doing the real work.&lt;/p&gt;

&lt;h3&gt;
  
  
  curl
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://rpc.swiftnodes.io/rpc/hyperliquid?key&lt;span class="o"&gt;=&lt;/span&gt;YOUR_API_KEY &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}'&lt;/span&gt;
&lt;span class="c"&gt;# 0x2c5382e  -&amp;gt; 46,479,405&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  viem
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createPublicClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parseUnits&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;viem&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;mainnet&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;viem/chains&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hyperliquid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;mainnet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;999&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hyperliquid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;rpcUrls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://rpc.swiftnodes.io/rpc/hyperliquid?key=YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createPublicClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;hyperliquid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;http&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;block&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBlockNumber&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ethers v6
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;JsonRpcProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ethers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;JsonRpcProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://rpc.swiftnodes.io/rpc/hyperliquid?key=YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;999&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getNetwork&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;   &lt;span class="c1"&gt;// { chainId: 999n, name: 'unknown' }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;eth_accounts&lt;/code&gt; returns &lt;code&gt;-32601 Method not found&lt;/code&gt; rather than an empty array, so anything that probes for unlocked accounts will take an exception rather than a length-0 list.&lt;/p&gt;

&lt;h2&gt;
  
  
  The block number is decorative
&lt;/h2&gt;

&lt;p&gt;This is the finding worth structuring your code around.&lt;/p&gt;

&lt;p&gt;Take a contract that was deployed at block &lt;strong&gt;46,479,401&lt;/strong&gt; — 261 blocks, about four minutes, before the head I was reading — and ask for its code at heights before that moment existed:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;
&lt;code&gt;eth_getCode&lt;/code&gt; block param&lt;/th&gt;
&lt;th&gt;Returned&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;46,479,400&lt;/code&gt; (1 block before deploy)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0x6080604052348015600e575f80fd5b50…&lt;/code&gt; — 342 chars&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;46,478,401&lt;/code&gt; (1,000 blocks before)&lt;/td&gt;
&lt;td&gt;same 342 chars&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;46,379,401&lt;/code&gt; (100,000 blocks before)&lt;/td&gt;
&lt;td&gt;same 342 chars&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;0x1&lt;/code&gt; (height one)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;same 342 chars&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A correct node must answer &lt;code&gt;0x&lt;/code&gt; for every height before the deployment, because the account did not exist. It answers with the current bytecode at all four. &lt;code&gt;eth_getBalance&lt;/code&gt; and &lt;code&gt;eth_getStorageAt&lt;/code&gt; behave the same way, and &lt;code&gt;eth_getBlockReceipts&lt;/code&gt; at height 1 errors with &lt;code&gt;invalid block height: 1&lt;/code&gt; — so the node clearly &lt;em&gt;knows&lt;/em&gt; block 1 is unavailable and still happily serves you "state" at block 1.&lt;/p&gt;

&lt;p&gt;Read that precisely: &lt;strong&gt;block-tagged state reads are answered from the latest block.&lt;/strong&gt; Not pruned-and-erroring, not approximate — silently wrong, and indistinguishable from a correct answer unless you test it.&lt;/p&gt;

&lt;p&gt;The probe, so you can run it against any provider you are evaluating:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;H&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://rpc.swiftnodes.io/rpc/hyperliquid?key&lt;span class="o"&gt;=&lt;/span&gt;YOUR_API_KEY
&lt;span class="c"&gt;# 1. find a contract deployed recently&lt;/span&gt;
&lt;span class="nv"&gt;R&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="nv"&gt;$H&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"jsonrpc":"2.0","id":1,"method":"eth_getTransactionReceipt","params":["&amp;lt;recent creation tx hash&amp;gt;"]}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'s/.*"contractAddress":"(0x[a-fA-F0-9]{40})".*/\1/'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# 2. ask for its code at height 1&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="nv"&gt;$H&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;jsonrpc&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;2.0&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:1,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;method&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;eth_getCode&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;params&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:[&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$R&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;0x1&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;]}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If step 2 returns anything other than &lt;code&gt;"result":"0x"&lt;/code&gt;, historical state is not being honoured — no matter what the endpoint advertises, and no matter what a health check that only looks at HTTP status tells you.&lt;/p&gt;

&lt;p&gt;Practical consequences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Never use a historical block tag for a real balance or nonce here.&lt;/strong&gt; If you need state as of a past block, you need an indexer that recorded it, not this RPC.&lt;/li&gt;
&lt;li&gt;Reorg-safe nonce/balance logic that pins &lt;code&gt;blockNumber&lt;/code&gt; "for consistency" gets no consistency guarantee — you are reading head under a label.&lt;/li&gt;
&lt;li&gt;Indexers built on &lt;code&gt;debug_*&lt;/code&gt;/archive traces cannot bootstrap from this endpoint. Event-log indexing is the path that works (see the caps below), because logs are fetched per block range and &lt;em&gt;are&lt;/em&gt; block-accurate.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's different from a mainstream EVM node
&lt;/h2&gt;

&lt;h3&gt;
  
  
  No WebSocket at all
&lt;/h3&gt;

&lt;p&gt;There is no &lt;code&gt;ws&lt;/code&gt;/&lt;code&gt;wss&lt;/code&gt; route for this chain on SwiftNodes, and &lt;code&gt;eth_subscribe&lt;/code&gt; over HTTP returns &lt;code&gt;-32601 Method not found&lt;/code&gt;. Nothing is half-open: subscriptions simply do not exist here. Poll at ~1 s to track ~1 block per poll, or use an external feed for low-latency updates. Do not assume the absence of a WS error means your subscription is working — check that you ever received a confirmation ID.&lt;/p&gt;

&lt;h3&gt;
  
  
  A base fee that barely moves
&lt;/h3&gt;

&lt;p&gt;Across eight sampled blocks, &lt;code&gt;baseFeePerGas&lt;/code&gt; was &lt;code&gt;100000000&lt;/code&gt; wei in every one — 0.1 gwei — and &lt;code&gt;gasLimit&lt;/code&gt; was pinned at 3,000,000. &lt;code&gt;eth_feeHistory&lt;/code&gt; does show slight variation (0x651a00b ≈ 0.106 gwei alongside 0x5f5e100), so the mechanism exists; it is simply clamped to within a few percent and does not respond to demand the way an L1 does. &lt;code&gt;eth_maxPriorityFeePerGas&lt;/code&gt; returns &lt;code&gt;0x0&lt;/code&gt;. So EIP-1559 fee &lt;em&gt;estimation&lt;/em&gt; is effectively meaningless here: there is no market to estimate. Transactions in the blocks I sampled were type-2 with a user-chosen &lt;code&gt;gasPrice&lt;/code&gt; of 1.6 gwei against a &lt;code&gt;maxFeePerGas&lt;/code&gt; of 30 gwei, so senders are setting their own ceiling and the near-fixed base fee just gets burned.&lt;/p&gt;

&lt;p&gt;The practical rule: hard-code a sane &lt;code&gt;maxFeePerGas&lt;/code&gt;, skip fee estimators, and treat &lt;code&gt;eth_gasPrice&lt;/code&gt; as a constant rather than a signal.&lt;/p&gt;

&lt;h3&gt;
  
  
  Block history is about two weeks
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;oldest readable block   ~45,269,661
head                     46,479,662
retained                  1,209,840 blocks   (~13.9 days at 0.985 s)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Heights older than that fail explicitly, which is the &lt;em&gt;good&lt;/em&gt; case:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;-32603&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"invalid block height: 1000000"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;earliest&lt;/code&gt; is not supported either — it comes back as &lt;code&gt;invalid block height: 0&lt;/code&gt;. Anything that expects a canonical &lt;code&gt;earliest&lt;/code&gt; tag needs a fallback to a numeric height.&lt;/p&gt;

&lt;h3&gt;
  
  
  eth_getLogs has two different ceilings
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Range&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;100 blocks&lt;/td&gt;
&lt;td&gt;1,055 logs, ~400 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;300 blocks&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{"code":-32008,"message":"Response is too big","data":"Exceeded max limit of 1048576"}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1,000 blocks&lt;/td&gt;
&lt;td&gt;same &lt;code&gt;-32008&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5,000 blocks&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{"code":-32602,"message":"query exceeds max block range 1000"}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two independent limits, and the &lt;strong&gt;byte cap bites first&lt;/strong&gt;. A 1,000-block window is nominally allowed but fails on any busy stretch, so "1000 blocks" is not the number to design around — the number is &lt;em&gt;whatever fits in 1 MiB&lt;/em&gt;, which on Hyperliquid means ~100 blocks during activity. Chunk by 50–100 blocks, and handle &lt;code&gt;-32008&lt;/code&gt; by halving the range rather than surfacing an error. Note also that the two error codes are different for the two causes, so a retry policy keyed on one will miss the other.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tracing is absent
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;debug_traceBlockByNumber&lt;/code&gt; → &lt;code&gt;does not exist/is not available&lt;/code&gt;; &lt;code&gt;trace_replayBlockTransactions&lt;/code&gt; and &lt;code&gt;txpool_status&lt;/code&gt; → &lt;code&gt;-32601&lt;/code&gt;. No simulation-adjacent introspection, no mempool visibility — which fits a chain whose execution is driven by an order book rather than a public mempool. If your stack has a &lt;code&gt;debug_traceTransaction&lt;/code&gt; fallback path, it will not fire here.&lt;/p&gt;

&lt;h3&gt;
  
  
  The transaction object carries an extra field
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;eth_getBlockByNumber&lt;/code&gt; with full transactions returns a &lt;code&gt;blockTimestamp&lt;/code&gt; on each tx alongside the standard fields. It is additive, so most clients ignore it; if you decode transactions into a strict schema, allow for the extra key.&lt;/p&gt;

&lt;h3&gt;
  
  
  Not every upstream behaves the same
&lt;/h3&gt;

&lt;p&gt;This route pools more than one Hyperliquid node. Against a busy &lt;code&gt;eth_getLogs&lt;/code&gt; window one answered &lt;code&gt;-32008 Response is too big&lt;/code&gt; and another answered &lt;code&gt;-32005 rate limited&lt;/code&gt;; the same split showed on &lt;code&gt;debug_traceBlockByNumber&lt;/code&gt;. If you are building retry or circuit-breaker logic, key it on the JSON-RPC &lt;code&gt;code&lt;/code&gt; rather than assuming one error shape per condition.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means for your stack
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Test the block tag before trusting it.&lt;/strong&gt; One &lt;code&gt;eth_getCode&lt;/code&gt; on a freshly deployed contract tells you whether an endpoint honours history. On this route it does not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design around ~100-block log chunks&lt;/strong&gt;, and treat "response too big" as a normal control signal, not a failure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not fee-estimate.&lt;/strong&gt; Fixed 0.1 gwei base fee, priority fee of zero.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Plan without WebSocket or tracing.&lt;/strong&gt; Poll at ~1 s; accept that mempool and debug surfaces do not exist.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And do not treat a 200 OK on a historical read as proof of anything&lt;/strong&gt; — that is the entire lesson of this endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;Hyperliquid's EVM is chain &lt;code&gt;999&lt;/code&gt;, blocks about every &lt;strong&gt;0.985 s&lt;/strong&gt;, a &lt;strong&gt;near-fixed 0.1 gwei base fee&lt;/strong&gt; with a 3,000,000 gas limit, and no priority-fee market. It has no WebSocket, no &lt;code&gt;debug_*&lt;/code&gt;/&lt;code&gt;trace_*&lt;/code&gt;/&lt;code&gt;txpool_*&lt;/code&gt;, and holds roughly &lt;strong&gt;1.21M blocks (~14 days)&lt;/strong&gt; of history, erroring cleanly beyond that. &lt;code&gt;eth_getLogs&lt;/code&gt; is bounded by both a 1,000-block range and a 1 MiB response, and the byte limit arrives first at about 100 blocks of activity. The important part: &lt;strong&gt;block-tagged state reads are answered from the latest block&lt;/strong&gt; — &lt;code&gt;eth_getCode&lt;/code&gt; for a contract deployed seconds ago returns its full bytecode at height &lt;code&gt;1&lt;/code&gt;, so historical balances, nonces and storage slots cannot be recovered here and will not warn you.&lt;/p&gt;

&lt;p&gt;For Hyperliquid RPC across load-balanced independent upstreams, &lt;a href="https://swiftnodes.io/" rel="noopener noreferrer"&gt;grab a free API key&lt;/a&gt; and point your client at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;https://rpc.swiftnodes.io/rpc/hyperliquid?key=YOUR_API_KEY
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One key covers all 86 chains, and the free tier is 2 requests per second — enough to run the &lt;code&gt;eth_getCode&lt;/code&gt; probe above against this and every other endpoint you rely on. That probe takes about ninety seconds and it is the only way to know whether the "archive" node you are paying for is telling you the truth about the past.&lt;/p&gt;

</description>
      <category>hyperliquid</category>
      <category>blockchain</category>
      <category>web3</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Neutron RPC: A Developer's Guide (neutron-1, CometBFT 0.38, No eth_*)</title>
      <dc:creator>SwiftNodes</dc:creator>
      <pubDate>Sun, 20 Sep 2026 11:28:01 +0000</pubDate>
      <link>https://dev.to/swiftnodes/neutron-rpc-a-developers-guide-neutron-1-cometbft-038-no-eth-4jjm</link>
      <guid>https://dev.to/swiftnodes/neutron-rpc-a-developers-guide-neutron-1-cometbft-038-no-eth-4jjm</guid>
      <description>&lt;p&gt;Neutron is not an EVM chain, and the RPC reflects that: there is no &lt;code&gt;eth_*&lt;/code&gt; surface at all, no numeric chain ID, and the request shape is CometBFT JSON-RPC rather than the Ethereum method set. None of that is surprising once you have read a Cosmos RPC reference — but each of these will break a piece of code you already have, and a few of them will not break it loudly.&lt;/p&gt;

&lt;p&gt;Everything below was measured on 2026-09-20 through &lt;code&gt;https://rpc.swiftnodes.io/rpc/neutron&lt;/code&gt;, against the same chain from a second independent endpoint for anything I did not want to take on trust. Re-run any of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The essentials
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Network / chain ID&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;neutron-1&lt;/code&gt; (a &lt;strong&gt;string&lt;/strong&gt; — there is no numeric chain ID)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consensus client&lt;/td&gt;
&lt;td&gt;CometBFT &lt;code&gt;0.38.19&lt;/code&gt; (protocol p2p 8, block 11, app 0)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Block time&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;~7.0 s measured this week&lt;/strong&gt;, ~2.05 s over the preceding months — see below&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Head at time of writing&lt;/td&gt;
&lt;td&gt;&lt;code&gt;61,612,831&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Validators&lt;/td&gt;
&lt;td&gt;11 active, 11 signatures in &lt;code&gt;last_commit&lt;/code&gt;, &lt;code&gt;ed25519&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Block limits&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;max_bytes&lt;/code&gt; 22,020,096 (~21 MiB), &lt;code&gt;max_gas&lt;/code&gt; 330,000,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EVM methods&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;None.&lt;/strong&gt; &lt;code&gt;eth_chainId&lt;/code&gt; → &lt;code&gt;-32601 Method not found&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://rpc.swiftnodes.io/rpc/neutron?key=YOUR_API_KEY&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Execution layer&lt;/td&gt;
&lt;td&gt;CosmWasm (&lt;code&gt;wasmvm&lt;/code&gt;) contracts, not the EVM&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Connecting: the setup
&lt;/h2&gt;

&lt;p&gt;CometBFT RPC is JSON-RPC 2.0 over POST, with the method name in the body. There are no &lt;code&gt;params&lt;/code&gt; arrays in the Ethereum sense — most methods take an object, and many take nothing.&lt;/p&gt;

&lt;h3&gt;
  
  
  curl
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# node + sync state&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://rpc.swiftnodes.io/rpc/neutron?key&lt;span class="o"&gt;=&lt;/span&gt;YOUR_API_KEY &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"jsonrpc":"2.0","id":1,"method":"status","params":{}}'&lt;/span&gt;

&lt;span class="c"&gt;# a specific block by height&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://rpc.swiftnodes.io/rpc/neutron?key&lt;span class="o"&gt;=&lt;/span&gt;YOUR_API_KEY &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"jsonrpc":"2.0","id":1,"method":"block","params":{"height":"61612831"}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The one thing to get right early: &lt;strong&gt;this path is POST-only.&lt;/strong&gt; A &lt;code&gt;GET /status&lt;/code&gt; — which works against a bare CometBFT node on port 26657 — returns &lt;code&gt;404&lt;/code&gt; here. Clients built around the Tendermint REST-style paths will need the JSON-RPC-over-POST form instead.&lt;/p&gt;

&lt;h3&gt;
  
  
  A tiny fetch helper
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;RPC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://rpc.swiftnodes.io/rpc/neutron?key=YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;RPC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;content-type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;jsonrpc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sync_info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;latest_block_height&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// 61612831 at time of writing&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;block&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;block&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chain_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;                 &lt;span class="c1"&gt;// "neutron-1"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the explicit error check. CometBFT signals most failures as a JSON-RPC &lt;code&gt;error&lt;/code&gt; object with &lt;strong&gt;HTTP 200&lt;/strong&gt;, so a client that only looks at status codes will treat "height is not available" as a successful response with a missing field.&lt;/p&gt;

&lt;p&gt;CosmJS users: &lt;code&gt;Tendermint37Client&lt;/code&gt; / &lt;code&gt;HttpBatchLink&lt;/code&gt; speak this same POST-to-root JSON-RPC, so the endpoint drops in. Verify the version handshake yourself rather than assuming — this node reports CometBFT 0.38.19, and the client you pick has to tolerate that.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's different from an EVM chain
&lt;/h2&gt;

&lt;h3&gt;
  
  
  There is no chain ID number
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;eth_chainId&lt;/code&gt; returns &lt;code&gt;-32601 Method not found&lt;/code&gt;, and our own status API reports &lt;code&gt;chainId: null&lt;/code&gt; for this chain. The identifier is the string &lt;code&gt;neutron-1&lt;/code&gt;, and it lives in &lt;code&gt;status.node_info.network&lt;/code&gt; and in every block header. Anything in your code that expects an integer — a &lt;code&gt;switch (chainId)&lt;/code&gt; on a config map, a wallet &lt;code&gt;addEthereumChain&lt;/code&gt; call, a chain-registry lookup keyed by number — needs a string key here.&lt;/p&gt;

&lt;h3&gt;
  
  
  Two header fields vanished in CometBFT 0.38
&lt;/h3&gt;

&lt;p&gt;The header I read at block 61,612,809 had exactly these keys:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version, chain_id, height, time, last_block_id, last_commit_hash, data_hash,
validators_hash, next_validators_hash, consensus_hash, app_hash,
last_results_hash, evidence_hash, proposer_address
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no &lt;code&gt;num_txs&lt;/code&gt; and no &lt;code&gt;total_txs&lt;/code&gt; — 0.38 removed them. Code that reads &lt;code&gt;header.num_txs&lt;/code&gt; gets &lt;code&gt;undefined&lt;/code&gt;, which in JavaScript means &lt;code&gt;NaN&lt;/code&gt; in arithmetic and a silently empty loop in a "skip empty blocks" filter. The replacement is the transaction list itself: &lt;code&gt;result.block.data.txs.length&lt;/code&gt; (the block I sampled contained 1 tx).&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;health&lt;/code&gt; returns an empty object
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"jsonrpc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"result"&lt;/span&gt;&lt;span class="p"&gt;:{}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No &lt;code&gt;is_node_healthy&lt;/code&gt; field, from either this route or the upstream directly — so a readiness probe that checks &lt;code&gt;result.is_node_healthy === true&lt;/code&gt; will read &lt;code&gt;undefined&lt;/code&gt; and fail, and one that checks &lt;code&gt;result.is_node_healthy !== false&lt;/code&gt; will pass on a node that is down. Use &lt;code&gt;status.sync_info.catching_up === false&lt;/code&gt; instead, and treat a failed &lt;code&gt;status&lt;/code&gt; call as unhealthy. &lt;code&gt;status.validator_info&lt;/code&gt; also came back &lt;code&gt;null&lt;/code&gt; here, so don't build on it either.&lt;/p&gt;

&lt;h3&gt;
  
  
  Block production is not what the docs say
&lt;/h3&gt;

&lt;p&gt;This is the one I would not have believed without measuring twice.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Window&lt;/th&gt;
&lt;th&gt;Measured&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;last 100,000 blocks (2026-09-12 → 09-20)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;7.02 s / block&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;last 5,000 blocks&lt;/td&gt;
&lt;td&gt;7.001 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;last 1,000 blocks&lt;/td&gt;
&lt;td&gt;6.995 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;last 200 blocks&lt;/td&gt;
&lt;td&gt;6.979 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;block 56,000,000 (2026-05-10) → head&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2.05 s / block&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Consecutive blocks were 7.40, 1.56, 7.41, 7.72, 7.45, 7.44 s apart — so this is a steady ~7 s cadence with occasional fast blocks, not one slow block skewing an average. I repeated the same measurement against a second, independent RPC and got 6.979 s and 7.001 s for the 200- and 2000-block windows, which rules out anything about the route in between.&lt;/p&gt;

&lt;p&gt;Read that as: Neutron's long-run rate is ~2 s, and as of this week it is producing at roughly 7 s. Anything you have tuned against a 2 s assumption — a poll interval, a "stale tip" alert threshold, a timeout budget, a UI's "synced" indicator, an ETA in blocks — is currently wrong by about 3.5×. If you have a fixed-interval poller, make it interval-based rather than block-count-based, and set the stall threshold from a measurement you take today, not from a spec.&lt;/p&gt;

&lt;h2&gt;
  
  
  History: the floor moves between nodes
&lt;/h2&gt;

&lt;p&gt;This is the part that will bite you in production.&lt;/p&gt;

&lt;p&gt;Requesting &lt;code&gt;block&lt;/code&gt; at height 1 does not return an empty result — it returns an explicit refusal naming the node's own retention floor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;-32603&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Internal error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"height 1 is not available, lowest height is 55481501"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, minutes later, the &lt;em&gt;same request through the same URL&lt;/em&gt; named a different floor: &lt;code&gt;61317000&lt;/code&gt;. And &lt;code&gt;block&lt;/code&gt; at 61,317,000 failed while 61,317,001 succeeded.&lt;/p&gt;

&lt;p&gt;That is not flakiness in the transport. A pool of independent RPC nodes each prune on their own schedule, so "how far back can I read?" has no single answer on a load-balanced endpoint. Observed floors on this route today spanned &lt;strong&gt;55,481,501 to 61,317,000&lt;/strong&gt; — a difference of ~5.8 million blocks, i.e. one node kept months more history than another.&lt;/p&gt;

&lt;p&gt;Practical consequences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A historical read may succeed and then fail on the next identical call. Design for it: retry, and if the query matters, pin it to a specific archival endpoint rather than a pooled one.&lt;/li&gt;
&lt;li&gt;Do not infer "archive" from a successful old-block read. One success means one node had it.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;?archive=1&lt;/code&gt; is an EVM routing hint on SwiftNodes; it does not apply to CometBFT chains like this one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For reference, the &lt;code&gt;genesis&lt;/code&gt; method reports &lt;code&gt;genesis_time: 2025-11-28T11:42:18Z&lt;/code&gt; with &lt;code&gt;initial_height: 1&lt;/code&gt; — which cannot be reconciled with a chain at height 61.6M. Treat the &lt;code&gt;genesis&lt;/code&gt; result as a consensus-parameter document (chain ID, validator set, upgrade heights), not as a reliable chain birthday. A third endpoint I probed returned no genesis at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  The methods you'll actually use
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;What it gives you&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;status&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;network, CometBFT version, head height/time, &lt;code&gt;catching_up&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;your real health check&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;block&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;header + txs + &lt;code&gt;last_commit&lt;/code&gt; for one height&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;params.height&lt;/code&gt; as a &lt;strong&gt;string&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;block_results&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;events and per-tx results for a height&lt;/td&gt;
&lt;td&gt;separate call from &lt;code&gt;block&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;blockchain&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;compact metadata for a height range&lt;/td&gt;
&lt;td&gt;cheap way to walk history&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tx&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;one transaction by hash, with proof&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tx_search&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;find txs by query&lt;/td&gt;
&lt;td&gt;see the warning below&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;validators&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;the set at a height&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;total&lt;/code&gt; came back 11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;consensus_params&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;block limits, evidence rules, vote-extensions height&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;broadcast_tx_sync&lt;/code&gt; / &lt;code&gt;_async&lt;/code&gt; / &lt;code&gt;_commit&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;submit a tx&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;_commit&lt;/code&gt; waits for inclusion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;abci_query&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;raw app state (contracts, params, stores)&lt;/td&gt;
&lt;td&gt;the Cosmos equivalent of a storage read&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;block&lt;/code&gt; and &lt;code&gt;block_results&lt;/code&gt; are two calls. Events — the thing you usually actually want — live in &lt;code&gt;block_results&lt;/code&gt;, so a poller that fetches only &lt;code&gt;block&lt;/code&gt; sees transactions with no results and reports nothing happened.&lt;/p&gt;

&lt;h3&gt;
  
  
  tx_search: some queries do not answer
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Query&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tx.height=61612805&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;129 ms, &lt;code&gt;total_count=1&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;message.action='/cosmwasm.wasm.v1.MsgExecuteContract'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;17,245 ms, &lt;code&gt;total_count=47532&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tx.height&amp;gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;28,066 ms, then &lt;code&gt;Backend node error&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Exact-height lookups are fast. Attribute searches are served, but slowly and at large result counts, and an unbounded range query does not return at all — it burns ~28 s and then errors. If you need "all txs matching X over a period", iterate strict heights or use an indexer; do not hand &lt;code&gt;tx_search&lt;/code&gt; a wide range and wait.&lt;/p&gt;

&lt;p&gt;On the dYdX route we also saw a stricter rule — &lt;code&gt;tx.height&amp;gt;0&lt;/code&gt; rejected outright with &lt;code&gt;-32701 "please specify tx.height event with strict equality"&lt;/code&gt;. Different operators configure the indexer differently, so treat &lt;code&gt;tx_search&lt;/code&gt; as capability you must probe per endpoint, not a guarantee of the RPC spec.&lt;/p&gt;

&lt;h2&gt;
  
  
  WebSocket
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;wss&lt;/code&gt; is available on this chain and our status API reports &lt;code&gt;wsAvailable: true&lt;/code&gt;. The subscription handshake is CometBFT's, not Ethereum's: method &lt;code&gt;subscribe&lt;/code&gt; with &lt;code&gt;query&lt;/code&gt;, e.g. &lt;code&gt;{"jsonrpc":"2.0","id":1,"method":"subscribe","params":{"query":"tm.event='NewBlockEvent'"}}&lt;/code&gt;, and events arrive as notifications on that subscription rather than as &lt;code&gt;eth_subscribe&lt;/code&gt; IDs. If your code already handles &lt;code&gt;eth_subscribe&lt;/code&gt;, this is a rewrite of the subscription layer, not a config change.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means for your stack
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Check your assumptions about time.&lt;/strong&gt; ~7 s blocks against a 2 s baseline changes every block-count-derived number in your system: staleness thresholds, retry backoffs, "confirmations", and how long a poll interval can be before you look dead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stop reading &lt;code&gt;num_txs&lt;/code&gt;.&lt;/strong&gt; It is gone in 0.38 and its absence is silent. Use &lt;code&gt;data.txs.length&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Treat &lt;code&gt;health&lt;/code&gt; as unusable&lt;/strong&gt; on this surface and gate readiness on &lt;code&gt;status.sync_info.catching_up&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Assume history is partial.&lt;/strong&gt; Ask the node what its floor is by requesting height 1 and reading the error, and do that at startup rather than discovering it on a user-facing query.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verify capability by calling it.&lt;/strong&gt; &lt;code&gt;eth_chainId&lt;/code&gt;, &lt;code&gt;tx_search&lt;/code&gt; ranges and old-block reads are three places where this chain told the truth only when asked directly. SwiftNodes publishes a weekly-measured method-support matrix for exactly this reason — the EVM half of it is at &lt;a href="https://swiftnodes.io/docs/method-support" rel="noopener noreferrer"&gt;Method support&lt;/a&gt;, and the full chain list is at &lt;a href="https://swiftnodes.io/chains" rel="noopener noreferrer"&gt;/chains&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;Neutron speaks CometBFT 0.38.19 JSON-RPC over POST on chain &lt;code&gt;neutron-1&lt;/code&gt;, with 11 validators, ~21 MiB blocks and a &lt;code&gt;max_gas&lt;/code&gt; of 330,000,000. There is no numeric chain ID and no &lt;code&gt;eth_*&lt;/code&gt; surface at all; &lt;code&gt;num_txs&lt;/code&gt; and &lt;code&gt;total_txs&lt;/code&gt; are gone from the header in 0.38; &lt;code&gt;health&lt;/code&gt; returns an empty object so readiness has to come from &lt;code&gt;status&lt;/code&gt;; &lt;code&gt;block&lt;/code&gt; and &lt;code&gt;block_results&lt;/code&gt; are separate calls; wide &lt;code&gt;tx_search&lt;/code&gt; queries time out where exact-height ones answer in ~130 ms; and the pruning floor differs between nodes in the same pool (55.48M to 61.32M observed today), so historical reads are not reliable on a pooled endpoint. The number to watch: blocks measured &lt;strong&gt;7.02 s&lt;/strong&gt; over the last 100k, against a &lt;strong&gt;2.05 s&lt;/strong&gt; average across the preceding five million — a live 3.5× slowdown, confirmed against a second independent RPC.&lt;/p&gt;

&lt;p&gt;For Neutron RPC across load-balanced independent upstreams, with HTTP, gRPC, REST and WebSocket, &lt;a href="https://swiftnodes.io/" rel="noopener noreferrer"&gt;grab a free API key&lt;/a&gt; and point your client at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;https://rpc.swiftnodes.io/rpc/neutron?key=YOUR_API_KEY
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One key covers all 86 chains, and the free tier is 2 requests per second — enough to run every probe above, including the block-time windows, and check this week's numbers for yourself.&lt;/p&gt;

</description>
      <category>cosmos</category>
      <category>blockchain</category>
      <category>web3</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Arbitrum RPC: A Developer's Guide (Chain ID 42161, ~250ms Blocks, Fake Gas Limit)</title>
      <dc:creator>SwiftNodes</dc:creator>
      <pubDate>Sat, 19 Sep 2026 07:13:42 +0000</pubDate>
      <link>https://dev.to/swiftnodes/arbitrum-rpc-a-developers-guide-chain-id-42161-250ms-blocks-fake-gas-limit-57gc</link>
      <guid>https://dev.to/swiftnodes/arbitrum-rpc-a-developers-guide-chain-id-42161-250ms-blocks-fake-gas-limit-57gc</guid>
      <description>&lt;p&gt;Arbitrum One is the rollup most Ethereum code runs on unmodified, and that is exactly the problem: it &lt;em&gt;does&lt;/em&gt; run, right up until a number stops meaning what your code assumes it means. The block gas limit is a placeholder. The priority fee is zero. &lt;code&gt;safe&lt;/code&gt; and &lt;code&gt;finalized&lt;/code&gt; move on a scale of minutes, not the seven days everyone associates with optimistic rollups. None of these will throw an error at you — they will just quietly make your dashboards, alerts and fee logic wrong.&lt;/p&gt;

&lt;p&gt;Everything below was measured today through &lt;code&gt;https://rpc.swiftnodes.io/rpc/arbitrum&lt;/code&gt;, so you can re-run every probe yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  The essentials
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Chain ID:&lt;/strong&gt; 42161 (&lt;code&gt;0xa4b1&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Block height at time of writing:&lt;/strong&gt; ~506,697,453&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Block time:&lt;/strong&gt; ~250 ms (measured 18 blocks in 4 seconds)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gas token:&lt;/strong&gt; ETH, 18 decimals&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Base fee:&lt;/strong&gt; ~0.02 gwei (0.020224 gwei measured)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Priority fee:&lt;/strong&gt; &lt;code&gt;eth_maxPriorityFeePerGas&lt;/code&gt; returns &lt;strong&gt;0&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Block gas limit:&lt;/strong&gt; 1,125,899,906,842,624 — that is exactly &lt;strong&gt;2⁵⁰&lt;/strong&gt;, a sentinel, not capacity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stack:&lt;/strong&gt; Nitro, sequencer-ordered, proofs posted to Ethereum&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WebSocket:&lt;/strong&gt; supported, &lt;code&gt;eth_subscribe&lt;/code&gt; verified working&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Archive state:&lt;/strong&gt; not served on this route — see Archive access
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The latency of the endpoint you pick matters more here than on most chains. At 250 ms blocks, a provider that adds 300 ms of round trip makes you feel every round trip roughly once per block.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting: the setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  curl
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Chain ID — expect 0xa4b1 (42161)&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://rpc.swiftnodes.io/rpc/arbitrum?key&lt;span class="o"&gt;=&lt;/span&gt;YOUR_API_KEY &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}'&lt;/span&gt;
&lt;span class="c"&gt;# -&amp;gt; {"jsonrpc":"2.0","id":1,"result":"0xa4b1"}&lt;/span&gt;

&lt;span class="c"&gt;# Latest block&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://rpc.swiftnodes.io/rpc/arbitrum?key&lt;span class="o"&gt;=&lt;/span&gt;YOUR_API_KEY &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'&lt;/span&gt;

&lt;span class="c"&gt;# Fee ceiling — the number you actually want&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://rpc.swiftnodes.io/rpc/arbitrum?key&lt;span class="o"&gt;=&lt;/span&gt;YOUR_API_KEY &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"jsonrpc":"2.0","method":"eth_gasPrice","params":[],"id":1}'&lt;/span&gt;
&lt;span class="c"&gt;# -&amp;gt; {"jsonrpc":"2.0","id":1,"result":"0x1336750"}   = 0.020145744 gwei&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  viem
&lt;/h3&gt;

&lt;p&gt;Stating the chain explicitly keeps working on every version, and it puts the field that matters in front of you:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createPublicClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defineChain&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;viem&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;arbitrum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineChain&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;42161&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Arbitrum One&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;nativeCurrency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Ether&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ETH&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;decimals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;rpcUrls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://rpc.swiftnodes.io/rpc/arbitrum?key=YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createPublicClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;arbitrum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;http&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blockNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBlockNumber&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;gasPrice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getGasPrice&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// ~0.02 gwei&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ethers v6
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;JsonRpcProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ethers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;JsonRpcProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://rpc.swiftnodes.io/rpc/arbitrum?key=YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="mi"&gt;42161&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBlockNumber&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What's the same as Ethereum
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;eth_*&lt;/code&gt; surface is complete and standard: &lt;code&gt;eth_call&lt;/code&gt;, &lt;code&gt;eth_getBalance&lt;/code&gt;, &lt;code&gt;eth_getLogs&lt;/code&gt;, &lt;code&gt;eth_getTransactionReceipt&lt;/code&gt;, &lt;code&gt;eth_sendRawTransaction&lt;/code&gt;, filters, &lt;code&gt;eth_feeHistory&lt;/code&gt;, the &lt;code&gt;pending&lt;/code&gt;/&lt;code&gt;safe&lt;/code&gt;/&lt;code&gt;finalized&lt;/code&gt; tags. Solidity contracts, ABIs, viem, ethers, Foundry and Hardhat all work as-is. That compatibility is genuinely excellent and it is why Arbitrum won.&lt;/p&gt;

&lt;p&gt;Blocks even carry the fields you expect, which is what makes the next section easy to miss.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's different from Ethereum
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The block gas limit is a sentinel
&lt;/h3&gt;

&lt;p&gt;Fetch a block and read &lt;code&gt;gasLimit&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://rpc.swiftnodes.io/rpc/arbitrum?key&lt;span class="o"&gt;=&lt;/span&gt;YOUR_API_KEY &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["latest",false],"id":1}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The block I pulled reported &lt;code&gt;gasLimit: 0x4000000000000&lt;/code&gt; — that is 1,125,899,906,842,624, exactly 2⁵⁰ — with &lt;code&gt;gasUsed&lt;/code&gt; of 398,374 in the same block. Any calculation of the form "this block is 0.0000354% full" or "I have 1.1 quadrillion gas of headroom" is nonsense. Nitro does not bound blocks the way L1 does; capacity is governed by the sequencer and by a separate per-second gas supply.&lt;/p&gt;

&lt;p&gt;Practical consequence: never size a transaction against the block gas limit here. Use &lt;code&gt;eth_estimateGas&lt;/code&gt; for the transaction you are sending, and treat the block's &lt;code&gt;gasLimit&lt;/code&gt; as decoration. If you run a mempool-style backlog monitor or a "block utilization" alert copied from an L1 dashboard, it is reporting a constant on Arbitrum.&lt;/p&gt;

&lt;h3&gt;
  
  
  Priority fee is zero, and that is not an error
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://rpc.swiftnodes.io/rpc/arbitrum?key&lt;span class="o"&gt;=&lt;/span&gt;YOUR_API_KEY &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"jsonrpc":"2.0","method":"eth_maxPriorityFeePerGas","params":[],"id":1}'&lt;/span&gt;
&lt;span class="c"&gt;# -&amp;gt; {"jsonrpc":"2.0","id":1,"result":"0x0"}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;eth_maxPriorityFeePerGas&lt;/code&gt; answers with &lt;code&gt;0x0&lt;/code&gt;. The RPC is not broken — the fee market is not a two-dimensional auction here, so there is no tip component to estimate. Inclusion is the sequencer's decision, not a bidding contest between validators.&lt;/p&gt;

&lt;p&gt;Two things follow. The common &lt;code&gt;maxFeePerGas = 2 × baseFee + tip&lt;/code&gt; recipe computes &lt;code&gt;2 × baseFee + 0&lt;/code&gt;, which is fine but adds nothing; and "bump the priority fee to unstick a stuck transaction" does not work as an operational lever on this chain. If your retry logic escalates tips, it is burning effort on a parameter the sequencer ignores. Read &lt;a href="https://swiftnodes.io/blog/estimating-gas-eth-estimategas-eip-1559" rel="noopener noreferrer"&gt;estimating gas properly&lt;/a&gt; for where these heuristics stop transferring.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;safe&lt;/code&gt; and &lt;code&gt;finalized&lt;/code&gt; are minutes, not days
&lt;/h3&gt;

&lt;p&gt;This is the one that surprises people most. Measured directly:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;tag&lt;/th&gt;
&lt;th&gt;block&lt;/th&gt;
&lt;th&gt;behind head&lt;/th&gt;
&lt;th&gt;~time behind&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;latest&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;506,697,453&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;safe&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;506,694,431&lt;/td&gt;
&lt;td&gt;3,022 blocks&lt;/td&gt;
&lt;td&gt;~13 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;finalized&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;506,692,973&lt;/td&gt;
&lt;td&gt;4,480 blocks&lt;/td&gt;
&lt;td&gt;~19 min&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;"Optimistic rollup means seven days" describes the &lt;strong&gt;challenge window for exiting to L1&lt;/strong&gt;, not how these block tags advance. They move on a minutes scale, tracking what nodes have confirmed among themselves. So if your code waits for &lt;code&gt;finalized&lt;/code&gt; believing it has crossed the dispute window, it is waiting roughly twenty minutes for a guarantee that the tag does not carry.&lt;/p&gt;

&lt;p&gt;For value-bearing operations, decide what you actually need. If you need "the sequencer will not reorder this", &lt;code&gt;safe&lt;/code&gt; is a reasonable marker. If you need "this is unarguably true on Ethereum", track the Rollup contract's confirmed roots on L1 — a different query entirely. &lt;a href="https://swiftnodes.io/blog/l2-finality-soft-vs-hard" rel="noopener noreferrer"&gt;Soft vs hard finality on L2s&lt;/a&gt; covers the distinction.&lt;/p&gt;

&lt;h3&gt;
  
  
  Consensus fields are placeholders
&lt;/h3&gt;

&lt;p&gt;The same block object carries &lt;code&gt;difficulty: 0x1&lt;/code&gt;, &lt;code&gt;nonce: 0x0000000000273449&lt;/code&gt;, and &lt;code&gt;uncles: []&lt;/code&gt;. There is no proof of work and no uncle mechanism; these are fixed or structural values kept so L1-shaped code keeps parsing. It also carries one field L1 blocks do not have: &lt;code&gt;l1BlockNumber&lt;/code&gt;, an explicit pointer into Ethereum. That pairing is useful — it is how you tie an Arbitrum block back to L1 context — but do not expect it from tools written against mainnet types.&lt;/p&gt;

&lt;h3&gt;
  
  
  System addresses answer with &lt;code&gt;0xfe&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;A check written as "does code exist at this address?" behaves oddly. Querying Arbitrum's system addresses returned the single byte &lt;code&gt;0xfe&lt;/code&gt; rather than contract bytecode, while a nearby unused address returned plain &lt;code&gt;0x&lt;/code&gt;. So a naive non-empty-code test reads as "something is deployed here" and gives you nothing to decode. Check the specific address list you care about rather than inferring from code size.&lt;/p&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;arb_*&lt;/code&gt; namespace may not be there
&lt;/h3&gt;

&lt;p&gt;Every Arbitrum-specific method I tried through our endpoint was refused:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;-32601&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"the method arb_getStorageRoot does not exist/is not available"&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That applies to the &lt;code&gt;arb_*&lt;/code&gt; names I probed (&lt;code&gt;arb_getBlockReceipts&lt;/code&gt;, &lt;code&gt;arb_getStorageRoot&lt;/code&gt;, &lt;code&gt;arb_getChainConfig&lt;/code&gt;, &lt;code&gt;arb_getNodeInterfaceVersion&lt;/code&gt;, &lt;code&gt;arb_getAddressTable&lt;/code&gt;). Availability of this namespace is a provider-by-provider decision, so treat it as absent until you have confirmed it on your own endpoint — and if you are migrating from a provider that exposed it, that migration is where things break, not in the standard &lt;code&gt;eth_*&lt;/code&gt; calls. The &lt;code&gt;-32601&lt;/code&gt; shape is covered in &lt;a href="https://swiftnodes.io/blog/json-rpc-error-codes-decoded" rel="noopener noreferrer"&gt;JSON-RPC error codes, decoded&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The methods you'll actually use
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Arbitrum note&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Current height&lt;/td&gt;
&lt;td&gt;&lt;code&gt;eth_blockNumber&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~250 ms blocks; height moves fast&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read contract&lt;/td&gt;
&lt;td&gt;&lt;code&gt;eth_call&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;standard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fee ceiling&lt;/td&gt;
&lt;td&gt;&lt;code&gt;eth_gasPrice&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~0.02 gwei&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tip&lt;/td&gt;
&lt;td&gt;&lt;code&gt;eth_maxPriorityFeePerGas&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;returns 0; not a lever&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gas units&lt;/td&gt;
&lt;td&gt;&lt;code&gt;eth_estimateGas&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;do &lt;strong&gt;not&lt;/strong&gt; compare to block gasLimit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Events&lt;/td&gt;
&lt;td&gt;&lt;code&gt;eth_getLogs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;bound ranges — &lt;a href="https://swiftnodes.io/blog/eth-getlogs-range-caps" rel="noopener noreferrer"&gt;range caps&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;L1 mapping&lt;/td&gt;
&lt;td&gt;block &lt;code&gt;l1BlockNumber&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Arbitrum-specific extra field&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Balance&lt;/td&gt;
&lt;td&gt;&lt;code&gt;eth_getBalance&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;18 decimals&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Nothing exotic; the traps are all in what the numbers mean. The general &lt;a href="https://swiftnodes.io/blog/what-is-an-rpc-endpoint" rel="noopener noreferrer"&gt;RPC endpoint primer&lt;/a&gt; and &lt;a href="https://swiftnodes.io/blog/ethereum-rpc-developer-reference" rel="noopener noreferrer"&gt;the Ethereum reference&lt;/a&gt; are the baseline this one is a diff against.&lt;/p&gt;

&lt;h2&gt;
  
  
  Archive access
&lt;/h2&gt;

&lt;p&gt;Be deliberate here if your workload reads history, because Arbitrum archive is a weak point right now.&lt;/p&gt;

&lt;p&gt;A state read at an early block on the default route came back with a provider's own gating message rather than data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Archive requests require a personal token. Get one at: https://www.allnodes.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and forcing archive routing with &lt;code&gt;?archive=1&lt;/code&gt; returned a malformed upstream body instead of a clean error. Two lessons, both general:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Verify archive with a real historical query before you build on it.&lt;/strong&gt; Ask for &lt;code&gt;eth_getBalance&lt;/code&gt; at block 2 and see what comes back. A provider that serves &lt;code&gt;eth_blockNumber&lt;/code&gt; beautifully may not be keeping state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An error's status code can lie about its category.&lt;/strong&gt; "Archive requests require a personal token" is a billing statement wearing the clothes of an RPC failure. Distinguishing capability limits from transient faults is the whole game in retry design — &lt;a href="https://swiftnodes.io/blog/rpc-retry-backoff" rel="noopener noreferrer"&gt;retry and backoff&lt;/a&gt; covers the taxonomy, and &lt;a href="https://swiftnodes.io/blog/full-node-vs-archive-node" rel="noopener noreferrer"&gt;full node vs archive node&lt;/a&gt; explains why this tier costs what it costs.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Recent-state reads, receipts and logs across recent ranges work normally. If you need deep historical state on Arbitrum, confirm it with your provider explicitly rather than assuming from the word "archive" appearing on a pricing page.&lt;/p&gt;

&lt;h2&gt;
  
  
  WebSocket subscriptions
&lt;/h2&gt;

&lt;p&gt;Verified end to end on this chain — subscribe, then a real head within about a second:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;+414ms   OPEN
&lt;/span&gt;&lt;span class="gp"&gt;+620ms   eth_subscribe -&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;0x77888af906b7608f…
&lt;span class="go"&gt;+743ms   newHeads notification, block 506,697,680
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wss://rpc.swiftnodes.io/ws/arbitrum?key=YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onopen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;jsonrpc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;eth_subscribe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;newHeads&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At 250 ms blocks, treat notifications as a signal to re-read rather than a queue you must not miss — you will drop heads during any reconnect. &lt;a href="https://swiftnodes.io/blog/websocket-reconnect-without-losing-events" rel="noopener noreferrer"&gt;Surviving WebSocket reconnects without losing events&lt;/a&gt; is the pattern, and &lt;a href="https://swiftnodes.io/blog/arbitrum-websocket-gotchas" rel="noopener noreferrer"&gt;our Arbitrum WebSocket write-up&lt;/a&gt; covers this chain's specific failure modes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compared to the L2s around it
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Arbitrum One&lt;/th&gt;
&lt;th&gt;Base&lt;/th&gt;
&lt;th&gt;Polygon zkEVM&lt;/th&gt;
&lt;th&gt;Gnosis Chain&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Chain ID&lt;/td&gt;
&lt;td&gt;42161&lt;/td&gt;
&lt;td&gt;8453&lt;/td&gt;
&lt;td&gt;1101&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Block time&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~250 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~2 s&lt;/td&gt;
&lt;td&gt;~1 s post-batches&lt;/td&gt;
&lt;td&gt;~5 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Block gasLimit&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2⁵⁰ sentinel&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;real limit&lt;/td&gt;
&lt;td&gt;real limit&lt;/td&gt;
&lt;td&gt;real limit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Priority fee&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;normal&lt;/td&gt;
&lt;td&gt;normal&lt;/td&gt;
&lt;td&gt;normal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;finalized&lt;/code&gt; behind&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~19 min&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~1 s&lt;/td&gt;
&lt;td&gt;proof-based&lt;/td&gt;
&lt;td&gt;~5 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EVM&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Base shares the sequencer model but not the fee shape or the sentinel limit. The zkEVM columns are in &lt;a href="https://swiftnodes.io/blog/polygon-zkevm-rpc-developer-guide" rel="noopener noreferrer"&gt;its own guide&lt;/a&gt;, and the sequencer concept behind all of these is unpacked in &lt;a href="https://swiftnodes.io/blog/what-is-a-sequencer" rel="noopener noreferrer"&gt;what a sequencer is&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production considerations
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Poll like you mean it.&lt;/strong&gt; At 250 ms blocks a 12-second poll interval is roughly 48 blocks of blind spot. Either subscribe or shorten the interval, and bound requests with &lt;a href="https://swiftnodes.io/blog/json-rpc-batching" rel="noopener noreferrer"&gt;JSON-RPC batching&lt;/a&gt; where you need many reads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Never derive capacity from &lt;code&gt;gasLimit&lt;/code&gt;.&lt;/strong&gt; It is a constant. Estimate per transaction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stop escalating tips.&lt;/strong&gt; They are zero here by design; use &lt;code&gt;eth_gasPrice&lt;/code&gt; and a margin.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Re-label what "finalized" means in your own code.&lt;/strong&gt; If a risk model treats it as the seven-day window, it is 20 minutes of comfort instead of a week of certainty.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Index with confirmations you can defend.&lt;/strong&gt; Chain reorganisation here is sequencer-level, not PoW-style; &lt;a href="https://swiftnodes.io/blog/handling-chain-reorgs-indexer" rel="noopener noreferrer"&gt;handling chain reorgs in indexers&lt;/a&gt; is the pattern.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Confirm archive before depending on it&lt;/strong&gt;, as above, on any chain you choose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing an Arbitrum RPC endpoint
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. What is its end-to-end latency to you?&lt;/strong&gt; On a 250 ms chain, the round trip is the whole user experience. Measure it yourself from your own region rather than trusting a marketed number — &lt;a href="https://swiftnodes.io/blog/public-rpc-vs-paid-rpc" rel="noopener noreferrer"&gt;public versus paid endpoints&lt;/a&gt; covers what actually moves once you are past the free tier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. What happens when its upstream is behind or rate-limited?&lt;/strong&gt; A single upstream gives you its outages and its 429s. Failover across independent endpoints is the difference between a blip and an incident, which is the point of routing through a layer that tries several upstreams per request and benches any that fall behind the chain head.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Will it tell you the truth about capabilities?&lt;/strong&gt; The good answer is a measured one. Check &lt;code&gt;eth_getCode&lt;/code&gt; on a chain-specific address, try an &lt;code&gt;arb_*&lt;/code&gt; method, request a balance at block 2. A provider that admits what it does not serve saves you a production discovery.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;Arbitrum One (chain ID 42161) speaks standard &lt;code&gt;eth_*&lt;/code&gt; on ~250 ms blocks, so nearly all Ethereum code and tooling works unchanged. What changes is meaning: the block &lt;code&gt;gasLimit&lt;/code&gt; is the sentinel 2⁵⁰ rather than capacity, &lt;code&gt;eth_maxPriorityFeePerGas&lt;/code&gt; returns 0 because inclusion is sequenced not auctioned, &lt;code&gt;difficulty&lt;/code&gt; and &lt;code&gt;nonce&lt;/code&gt; are placeholders, blocks carry an extra &lt;code&gt;l1BlockNumber&lt;/code&gt;, &lt;code&gt;arb_*&lt;/code&gt; methods may be refused, and &lt;code&gt;safe&lt;/code&gt;/&lt;code&gt;finalized&lt;/code&gt; sat about 3,022 and 4,480 blocks behind head — roughly 13 and 19 minutes — not the seven-day dispute window. Archive state reads are currently the weak spot on this route, so verify any historical read with a real query.&lt;/p&gt;

&lt;p&gt;For Arbitrum RPC with per-request failover across independent upstreams and a working &lt;code&gt;eth_subscribe&lt;/code&gt;, &lt;a href="https://swiftnodes.io/" rel="noopener noreferrer"&gt;grab a free API key&lt;/a&gt; and point your app at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://rpc.swiftnodes.io/rpc/arbitrum?key=YOUR_API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One key covers all 75 chains, and the free tier is 2 requests per second — enough to run every probe above, including the finality measurements, while you check the numbers for yourself.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://swiftnodes.io/blog/arbitrum-rpc-developer-guide" rel="noopener noreferrer"&gt;SwiftNodes blog&lt;/a&gt;. SwiftNodes provides flat-rate multi-chain RPC endpoints — HTTP + WebSocket, 75+ chains, no per-request metering. &lt;a href="https://swiftnodes.io/" rel="noopener noreferrer"&gt;Grab a free key&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ethereum</category>
      <category>blockchain</category>
      <category>web3</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Ethereum Classic RPC: A Developer's Guide (Chain ID 61, No EIP-1559)</title>
      <dc:creator>SwiftNodes</dc:creator>
      <pubDate>Fri, 18 Sep 2026 10:46:14 +0000</pubDate>
      <link>https://dev.to/swiftnodes/ethereum-classic-rpc-a-developers-guide-chain-id-61-no-eip-1559-3pic</link>
      <guid>https://dev.to/swiftnodes/ethereum-classic-rpc-a-developers-guide-chain-id-61-no-eip-1559-3pic</guid>
      <description>&lt;p&gt;Ethereum Classic is the awkward one in the EVM family. It isn't a rollup, isn't a sidechain, and didn't fork off Ethereum's design — it &lt;em&gt;is&lt;/em&gt; the original Ethereum chain, continuing the history that the majority of the community abandoned at block 1,920,000 in July 2016. Every block before the fork is literally the same blocks, with the same hashes, on both chains.&lt;/p&gt;

&lt;p&gt;For a developer, that shared lineage creates a specific set of assumptions that quietly stop being true. The fee market is the big one: Ethereum Classic never activated EIP-1559, so blocks carry no base fee, and the transaction-fee code you copied from an Ethereum tutorial is solving a problem this chain doesn't have. The other is finality: it's still proof-of-work, so there is no &lt;code&gt;finalized&lt;/code&gt; block to wait for.&lt;/p&gt;

&lt;p&gt;Everything below is measured through &lt;code&gt;https://rpc.swiftnodes.io/rpc/etc&lt;/code&gt; on 2026-09-18, so you can re-run each probe yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  The essentials
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Chain ID:&lt;/strong&gt; 61 (&lt;code&gt;0x3d&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Block height at time of writing:&lt;/strong&gt; 25,367,599 (&lt;code&gt;0x183142f&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Block time:&lt;/strong&gt; ~13–15 seconds (we measured 3 blocks across 45 seconds)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gas token:&lt;/strong&gt; ETC, 18 decimals&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consensus:&lt;/strong&gt; Proof of work (Etchash)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fee market:&lt;/strong&gt; Legacy &lt;code&gt;gasPrice&lt;/code&gt;. No EIP-1559 base fee&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reference client:&lt;/strong&gt; Core Geth — &lt;code&gt;eth_getBlockByNumber&lt;/code&gt; is served by &lt;code&gt;CoreGeth/hebeblock/v1.12.20-stable&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gas limit:&lt;/strong&gt; 8,000,512 per block (&lt;code&gt;0x7a1200&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Archive history:&lt;/strong&gt; Not available on our endpoints — see Archive access
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The block limit is worth noticing: 8,000,512 gas per block, against Ethereum's 36 million. Your largest single transactions need to fit inside that, and a contract that pushes a block-size boundary on Ethereum may behave differently here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting: the setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  curl
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Chain ID — expect 0x3d (61), not 0x1&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://rpc.swiftnodes.io/rpc/etc?key&lt;span class="o"&gt;=&lt;/span&gt;YOUR_API_KEY &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}'&lt;/span&gt;
&lt;span class="c"&gt;# -&amp;gt; {"jsonrpc":"2.0","id":1,"result":"0x3d"}&lt;/span&gt;

&lt;span class="c"&gt;# Latest block&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://rpc.swiftnodes.io/rpc/etc?key&lt;span class="o"&gt;=&lt;/span&gt;YOUR_API_KEY &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'&lt;/span&gt;

&lt;span class="c"&gt;# Legacy gas price — this is the fee number you actually use&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://rpc.swiftnodes.io/rpc/etc?key&lt;span class="o"&gt;=&lt;/span&gt;YOUR_API_KEY &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"jsonrpc":"2.0","method":"eth_gasPrice","params":[],"id":1}'&lt;/span&gt;
&lt;span class="c"&gt;# -&amp;gt; {"jsonrpc":"2.0","id":1,"result":"0x28fa6ae00"}   = 11 gwei&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  viem
&lt;/h3&gt;

&lt;p&gt;Most tooling has an entry for Ethereum Classic, but the safest portable form is to state the chain explicitly — this works regardless of which version you're on, and it makes the one field that matters (&lt;code&gt;chainId: 61&lt;/code&gt;) visible:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createPublicClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defineChain&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;viem&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;classic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineChain&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;61&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Ethereum Classic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;nativeCurrency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ETC&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ETC&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;decimals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;rpcUrls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://rpc.swiftnodes.io/rpc/etc?key=YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createPublicClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;classic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;http&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blockNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBlockNumber&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;gasPrice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getGasPrice&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// 11 gwei at time of writing&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ethers v6
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;JsonRpcProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ethers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;JsonRpcProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://rpc.swiftnodes.io/rpc/etc?key=YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="mi"&gt;61&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// chainId — pinning it stops the provider silently trusting a mismatched node&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBlockNumber&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pinning the &lt;code&gt;chainId&lt;/code&gt; in ethers is worth the one argument. Because ETC and ETH share a genesis and most of their history, a mislabelled endpoint is easy to mistake for a working one.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's the same as Ethereum
&lt;/h2&gt;

&lt;p&gt;The RPC surface is standard &lt;code&gt;eth_*&lt;/code&gt;. Nothing exotic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Same methods.&lt;/strong&gt; &lt;code&gt;eth_call&lt;/code&gt;, &lt;code&gt;eth_getBalance&lt;/code&gt;, &lt;code&gt;eth_getLogs&lt;/code&gt;, &lt;code&gt;eth_getTransactionReceipt&lt;/code&gt;, &lt;code&gt;eth_sendRawTransaction&lt;/code&gt; all behave as documented for Ethereum.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Same ABI and tooling.&lt;/strong&gt; Solidity compiles and deploys unchanged. Foundry, Hardhat, viem, ethers all work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Same precompiles, including the bn256 pairings.&lt;/strong&gt; This is the one I expected to be a problem and it isn't. An empty &lt;code&gt;eth_call&lt;/code&gt; to the pairing precompile returns the vacuously-true result on both chains:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ecPairing (0x08) with empty input -&amp;gt; 0x...01 on both chains&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://rpc.swiftnodes.io/rpc/etc?key&lt;span class="o"&gt;=&lt;/span&gt;YOUR_API_KEY &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"jsonrpc":"2.0","method":"eth_call","params":[{"to":"0x0000000000000000000000000000000000000008","input":"0x"},"latest"],"id":1}'&lt;/span&gt;
&lt;span class="c"&gt;# -&amp;gt; {"jsonrpc":"2.0","id":1,"result":"0x0000...0001"}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Identical output from &lt;code&gt;/rpc/eth&lt;/code&gt; and &lt;code&gt;/rpc/base&lt;/code&gt;. Snark-verification libraries and anything leaning on &lt;code&gt;ecadd&lt;/code&gt;/&lt;code&gt;ecmul&lt;/code&gt;/&lt;code&gt;ecpairing&lt;/code&gt; will not be the thing that breaks you here.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Same receipt and log semantics,&lt;/strong&gt; including &lt;code&gt;logsBloom&lt;/code&gt; and uncle handling.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's different from Ethereum
&lt;/h2&gt;

&lt;h3&gt;
  
  
  There is no base fee
&lt;/h3&gt;

&lt;p&gt;Ask for the latest block and count the fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://rpc.swiftnodes.io/rpc/etc?key&lt;span class="o"&gt;=&lt;/span&gt;YOUR_API_KEY &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["latest",false],"id":1}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The returned object includes &lt;code&gt;difficulty&lt;/code&gt;, &lt;code&gt;mixHash&lt;/code&gt;, &lt;code&gt;nonce&lt;/code&gt;, &lt;code&gt;miner&lt;/code&gt;, &lt;code&gt;totalDifficulty&lt;/code&gt;, &lt;code&gt;sha3Uncles&lt;/code&gt; and &lt;code&gt;uncles&lt;/code&gt;. It does &lt;strong&gt;not&lt;/strong&gt; include &lt;code&gt;baseFeePerGas&lt;/code&gt;. That absence is the whole story:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"number"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0x183142f"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"gasLimit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0x7a1200"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"difficulty"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0x6c54c9424b9f9"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"miner"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0x…"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mixHash"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0x…"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"nonce"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0x…"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;no&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;baseFeePerGas&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;all&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The familiar &lt;code&gt;maxFeePerGas = 2 × baseFee + tip&lt;/code&gt; recipe has nothing to multiply. Use &lt;code&gt;eth_gasPrice&lt;/code&gt; and send a legacy transaction.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;eth_maxPriorityFeePerGas&lt;/code&gt; answers — and that answer is a trap
&lt;/h3&gt;

&lt;p&gt;This one deserves its own warning, because it will not fail loudly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://rpc.swiftnodes.io/rpc/etc?key&lt;span class="o"&gt;=&lt;/span&gt;YOUR_API_KEY &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"jsonrpc":"2.0","method":"eth_maxPriorityFeePerGas","params":[],"id":1}'&lt;/span&gt;
&lt;span class="c"&gt;# -&amp;gt; {"jsonrpc":"2.0","id":1,"result":"0x28fa6ae00"}   = 11 gwei&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It returns &lt;code&gt;0x28fa6ae00&lt;/code&gt; — &lt;strong&gt;the exact same value as &lt;code&gt;eth_gasPrice&lt;/code&gt;.&lt;/strong&gt; The node is handing back its legacy gas-price suggestion through the 1559 method name. It is not a tip, and it is not a priority fee. Code that calls it, believes the name, and then sets &lt;code&gt;maxFeePerGas&lt;/code&gt; to &lt;code&gt;tip * 2&lt;/code&gt; will produce a meaningless fee ceiling on this chain — and the estimation call won't stop you, because &lt;code&gt;eth_estimateGas&lt;/code&gt; happily accepts EIP-1559-shaped transaction objects and returns a normal result.&lt;/p&gt;

&lt;p&gt;Read the fee from &lt;code&gt;eth_gasPrice&lt;/code&gt;. Treat the 1559 methods as absent even though one of them replies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Finality is probabilistic
&lt;/h3&gt;

&lt;p&gt;Proof of work means uncles, and uncles mean the head block can be superseded. There is no &lt;code&gt;finalized&lt;/code&gt; block tag to wait on here, and no challenge window to reason about either — that's an optimistic-rollup concept. Your tool is confirmation count, and it needs to be larger than your reflex Ethereum number because blocks arrive roughly every 13–15 seconds rather than every 12.&lt;/p&gt;

&lt;p&gt;For anything holding value, index and display the block you were included in and wait out a reorg, rather than assuming the latest block is durable. The mechanics are identical to any other PoW chain — &lt;a href="https://swiftnodes.io/blog/handling-chain-reorgs-indexer" rel="noopener noreferrer"&gt;our guide to handling reorgs in indexers&lt;/a&gt; covers the pattern.&lt;/p&gt;

&lt;h3&gt;
  
  
  Block numbers below 1,920,000 mean two different things
&lt;/h3&gt;

&lt;p&gt;This is a data-modelling bug rather than an RPC bug, and ETC is the only chain where you'll hit it. Because the chains share history, block hashes below the fork are byte-identical. Ask both chains for block 1,919,999 — the last block before the fork:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;c &lt;span class="k"&gt;in &lt;/span&gt;etc eth&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://rpc.swiftnodes.io/rpc/&lt;span class="nv"&gt;$c&lt;/span&gt;?key&lt;span class="o"&gt;=&lt;/span&gt;YOUR_API_KEY &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["0x1d4bff",false],"id":1}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    | python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import json,sys; print('&lt;/span&gt;&lt;span class="nv"&gt;$c&lt;/span&gt;&lt;span class="s2"&gt;', json.load(sys.stdin)['result']['hash'])"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both return the same hash. Then ask for the next block:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Block&lt;/th&gt;
&lt;th&gt;Ethereum Classic&lt;/th&gt;
&lt;th&gt;Ethereum&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1,919,999&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0xa218e2c611f21232d857e3c8…&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0xa218e2c611f21232d857e3c8…&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;identical&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1,920,000&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0x94365e3a8c0b35089c1d1195…&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0x4985f5ca3d2afbec36529aa9…&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;diverged&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1,920,001&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0xab7668dfd3bedcf9da505d69…&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0x87b2bc3f12e3ded808c6d4b9…&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;diverged&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;One block, and the two chains never speak to each other again. The genesis hash is likewise identical (&lt;code&gt;0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3&lt;/code&gt;), as is every block up to the split.&lt;/p&gt;

&lt;p&gt;So if you store transactions keyed on hash, or block heights without a chain discriminator, pre-fork records from Ethereum and Ethereum Classic will collide. A &lt;code&gt;(chainId, blockNumber)&lt;/code&gt; or &lt;code&gt;(chainId, txHash)&lt;/code&gt; composite key is the fix. This matters most if you run a multi-chain indexer, an airdrop/claims system, or anything with a transaction allowlist.&lt;/p&gt;

&lt;h2&gt;
  
  
  The methods you'll actually use
&lt;/h2&gt;

&lt;p&gt;Nothing exotic. The list you'd use on Ethereum, with one substitution in the fee path:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Note for ETC&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Current height&lt;/td&gt;
&lt;td&gt;&lt;code&gt;eth_blockNumber&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read a contract&lt;/td&gt;
&lt;td&gt;&lt;code&gt;eth_call&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fee estimate&lt;/td&gt;
&lt;td&gt;&lt;code&gt;eth_gasPrice&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;use this&lt;/strong&gt;, not &lt;code&gt;eth_maxPriorityFeePerGas&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gas units&lt;/td&gt;
&lt;td&gt;&lt;code&gt;eth_estimateGas&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;works normally&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Events&lt;/td&gt;
&lt;td&gt;&lt;code&gt;eth_getLogs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;keep ranges bounded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Receipt&lt;/td&gt;
&lt;td&gt;&lt;code&gt;eth_getTransactionReceipt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;check &lt;code&gt;status&lt;/code&gt;, expect reorgs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Send&lt;/td&gt;
&lt;td&gt;&lt;code&gt;eth_sendRawTransaction&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;legacy &lt;code&gt;gasPrice&lt;/code&gt; transaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Balance&lt;/td&gt;
&lt;td&gt;&lt;code&gt;eth_getBalance&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;18 decimals&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For decoding events and reading balances, the mechanics are unchanged from Ethereum — &lt;a href="https://swiftnodes.io/blog/reading-balances-eth-getbalance-balanceof" rel="noopener noreferrer"&gt;the balance-reading traps&lt;/a&gt; and &lt;a href="https://swiftnodes.io/blog/decoding-event-logs-topics-signatures" rel="noopener noreferrer"&gt;log decoding&lt;/a&gt; both apply exactly as written.&lt;/p&gt;

&lt;h2&gt;
  
  
  Archive access
&lt;/h2&gt;

&lt;p&gt;Be deliberate about this if your project reads history, because Ethereum Classic is not an archive chain on our service and the failure mode differs by how you ask:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# state from an early block&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://rpc.swiftnodes.io/rpc/etc?key=YOUR_API_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"jsonrpc":"2.0","method":"eth_getBalance","params":["0x0000000000000000000000000000000000000001","0x3e8"],"id":1}'&lt;/span&gt;
&lt;span class="c"&gt;# -&amp;gt; {"jsonrpc":"2.0","id":1,"error":{"code":-32000,&lt;/span&gt;
&lt;span class="c"&gt;#     "message":"missing trie node 97fb274dbf… state … is not available, not found"}}&lt;/span&gt;

&lt;span class="c"&gt;# explicitly asking for archive routing&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://rpc.swiftnodes.io/rpc/etc?key=YOUR_API_KEY&amp;amp;archive=1"&lt;/span&gt; ...
&lt;span class="c"&gt;# -&amp;gt; {"error":"No archive node available for this chain","chain":"etc"}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Recent-state reads — the last stretch of blocks — work as expected. Deep historical state returns &lt;code&gt;missing trie node&lt;/code&gt;, because the serving node is not keeping full historical state. Plain &lt;code&gt;eth_getBlockByNumber&lt;/code&gt; for old blocks is fine; it's state-at-height (&lt;code&gt;eth_getBalance&lt;/code&gt;, &lt;code&gt;eth_getCode&lt;/code&gt;, &lt;code&gt;eth_call&lt;/code&gt; at an old block) that isn't served.&lt;/p&gt;

&lt;p&gt;If your workload is genuinely archive-dependent — backtesting balances, replaying old state, rebuilding historical dashboards — Ethereum Classic is a chain where you should confirm archive availability with a provider before committing. That's a real limitation, not a paperwork one: &lt;a href="https://swiftnodes.io/blog/full-node-vs-archive-node" rel="noopener noreferrer"&gt;full node vs archive node&lt;/a&gt; explains what each tier costs and why providers draw the line where they do, and &lt;a href="https://swiftnodes.io/blog/eth-call-historical-block" rel="noopener noreferrer"&gt;reading historical state&lt;/a&gt; covers what you can still do without it.&lt;/p&gt;

&lt;h2&gt;
  
  
  WebSocket subscriptions: verify before you build on them
&lt;/h2&gt;

&lt;p&gt;The public status endpoint lists Ethereum Classic with &lt;code&gt;wsAvailable: true&lt;/code&gt; and &lt;code&gt;endpointTypes: ["http","ws"]&lt;/code&gt;, and the socket does accept a connection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wss://rpc.swiftnodes.io/ws/etc?key=YOUR_API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But when we tested it on 2026-09-18, an &lt;code&gt;eth_subscribe&lt;/code&gt; to &lt;code&gt;newHeads&lt;/code&gt; over that connection opened cleanly and then returned nothing — no subscription ID, no error — while the identical script against &lt;code&gt;/ws/eth&lt;/code&gt; came back with a subscription ID immediately. A connection that opens is not the same thing as a subscription that streams.&lt;/p&gt;

&lt;p&gt;So: treat WebSocket support on this chain as &lt;strong&gt;unverified on your provider&lt;/strong&gt;, and design the HTTP path first. Polling for heads is genuinely fine at a 13–15 second block time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;tip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBlockNumber&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;head&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBlockNumber&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;tip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;tip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="cm"&gt;/* process blocks tip-old..head */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;4000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Track the block height you have actually processed rather than the head you last saw, and re-read on each new head. On a PoW chain a head you were told about can still be replaced, so a subscription — where it works — is a signal to re-read, not an append-only stream. &lt;a href="https://swiftnodes.io/blog/websocket-reconnect-without-losing-events" rel="noopener noreferrer"&gt;Surviving WebSocket reconnects without losing events&lt;/a&gt; covers the resync pattern, and it applies equally to a polling loop that has to recover a gap.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compared to the chains around it
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Ethereum&lt;/th&gt;
&lt;th&gt;Ethereum Classic&lt;/th&gt;
&lt;th&gt;Polygon zkEVM&lt;/th&gt;
&lt;th&gt;Gnosis Chain&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Chain ID&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;61&lt;/td&gt;
&lt;td&gt;1101&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consensus&lt;/td&gt;
&lt;td&gt;PoS&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;PoW (Etchash)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Rollup + PoS&lt;/td&gt;
&lt;td&gt;PoS (GPOS)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EIP-1559 fee market&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;No&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;baseFeePerGas&lt;/code&gt; in blocks&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;No&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Block time&lt;/td&gt;
&lt;td&gt;~12s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~13–15s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~1s post-batches&lt;/td&gt;
&lt;td&gt;~5s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deterministic finality&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;No — probabilistic&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Proof-based&lt;/td&gt;
&lt;td&gt;~5 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EVM&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The row that surprises people is fee market, not consensus: you can port a contract to ETC without touching it, but the signing code needs a look. The other surprise is that ETC is &lt;em&gt;not&lt;/em&gt; an L2 — no sequencer, no forced-state inclusion, no withdrawal delay. &lt;a href="https://swiftnodes.io/blog/l2-finality-soft-vs-hard" rel="noopener noreferrer"&gt;L2 finality concepts&lt;/a&gt; don't apply; PoW confirmations do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production considerations
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Confirmations, not finalized tags.&lt;/strong&gt; Size your wait for a 13–15 second block time and probabilistic finality. Re-query on reorg.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pin the chain ID.&lt;/strong&gt; 61, everywhere it's accepted. Because pre-fork history is shared with Ethereum, a wrongly-configured endpoint can return plausible-looking data instead of an error.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gas price is a node suggestion.&lt;/strong&gt; &lt;code&gt;eth_gasPrice&lt;/code&gt; returns the node's own policy (11 gwei at time of writing) rather than a market-clearing auction price. Watch your backlog instead of trusting one number — &lt;a href="https://swiftnodes.io/blog/estimating-gas-eth-estimategas-eip-1559" rel="noopener noreferrer"&gt;estimating gas properly&lt;/a&gt; explains where the Ethereum-style heuristics stop transferring.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;eth_getLogs&lt;/code&gt; ranges.&lt;/strong&gt; Bound them, the same as anywhere else — &lt;a href="https://swiftnodes.io/blog/eth-getlogs-range-caps" rel="noopener noreferrer"&gt;range caps and how they bite&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No archive for this chain.&lt;/strong&gt; If your roadmap needs historical state on ETC, plan for it specifically rather than discovering it in production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Errors are the same errors.&lt;/strong&gt; If something fails, it will most likely look familiar — &lt;a href="https://swiftnodes.io/blog/json-rpc-error-codes-decoded" rel="noopener noreferrer"&gt;JSON-RPC error codes decoded&lt;/a&gt; is the reference.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing an Ethereum Classic RPC endpoint
&lt;/h2&gt;

&lt;p&gt;Three questions decide it:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Do you need historical state?&lt;/strong&gt; If yes, verify archive support for ETC before you build on it. Many providers advertise archive broadly while a given chain's node is not actually keeping full state.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Do you need WebSocket?&lt;/strong&gt; Don't take the checkbox on a provider's status page as a working subscription — open one and confirm it streams before you design around it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. What happens when it's slow?&lt;/strong&gt; A single upstream makes latency and downtime your problem. Failover across independent endpoints is what keeps a reader alive — which is the point of routing through a layer that tries several upstreams per request and benches any that fall behind the chain head.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;Ethereum Classic (chain ID 61) is a proof-of-work EVM chain sharing its entire history with Ethereum up to block 1,920,000 — so pre-fork block and transaction hashes are identical across both chains and need a chain discriminator in your schema. The RPC surface is standard &lt;code&gt;eth_*&lt;/code&gt;, Solidity and viem/ethers work unchanged, and even the bn256 precompiles are present. What is genuinely different: &lt;strong&gt;no EIP-1559&lt;/strong&gt; (blocks have no &lt;code&gt;baseFeePerGas&lt;/code&gt;, and &lt;code&gt;eth_maxPriorityFeePerGas&lt;/code&gt; returns the legacy &lt;code&gt;eth_gasPrice&lt;/code&gt; value — 11 gwei when measured — so use &lt;code&gt;eth_gasPrice&lt;/code&gt; and send legacy transactions), probabilistic finality instead of a &lt;code&gt;finalized&lt;/code&gt; block, ~13–15 second blocks under an 8,000,512 gas limit, and no archive state for this chain on our endpoints.&lt;/p&gt;

&lt;p&gt;For Ethereum Classic RPC with per-request failover across independent upstreams, &lt;a href="https://swiftnodes.io/" rel="noopener noreferrer"&gt;grab a free API key&lt;/a&gt; and point your app at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://rpc.swiftnodes.io/rpc/etc?key=YOUR_API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One key covers all 75 chains, and the free tier is 2 requests per second — enough to run every probe in this article while you verify the fork boundary for yourself.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://swiftnodes.io/blog/ethereum-classic-rpc-developer-guide" rel="noopener noreferrer"&gt;SwiftNodes blog&lt;/a&gt;. SwiftNodes provides flat-rate multi-chain RPC endpoints — HTTP + WebSocket, 75+ chains, no per-request metering. &lt;a href="https://swiftnodes.io/" rel="noopener noreferrer"&gt;Grab a free key&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ethereum</category>
      <category>blockchain</category>
      <category>web3</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
