<?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>What Is EIP-7702? EOAs That Act Like Smart Accounts</title>
      <dc:creator>SwiftNodes</dc:creator>
      <pubDate>Thu, 20 Aug 2026 06:52:12 +0000</pubDate>
      <link>https://dev.to/swiftnodes/what-is-eip-7702-eoas-that-act-like-smart-accounts-36o5</link>
      <guid>https://dev.to/swiftnodes/what-is-eip-7702-eoas-that-act-like-smart-accounts-36o5</guid>
      <description>&lt;p&gt;Run this against Ethereum mainnet right now:&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;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://rpc.swiftnodes.io/rpc/eth?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","id":1,"method":"eth_getCode",
       "params":["0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045","latest"]}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's vitalik.eth — an externally owned account, a plain wallet address. For ten years the correct answer was &lt;code&gt;0x&lt;/code&gt;: EOAs have no code, contracts do. Today it returns something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0xef01005a7fc11397e9a8ad41bf10bf13f22b0a63f96f6d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Twenty-three bytes of code on an EOA. That's EIP-7702, live on mainnet since the Pectra upgrade in May 2025, and it quietly breaks one of the oldest assumptions in Ethereum tooling. This post explains what it is, what those bytes mean, and what changes for you as a developer reading and writing over RPC.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem: EOAs are stuck, and ERC-4337 asked you to move
&lt;/h2&gt;

&lt;p&gt;Externally owned accounts have exactly one capability: a secp256k1 key signs transactions. No batching (approve + swap is always two transactions), no gas sponsorship (a new user with zero ETH can't do anything), no session keys, no spending limits, no recovery if the key leaks. Every one of those features requires logic, and EOAs can't hold logic.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://swiftnodes.io/blog/what-is-account-abstraction-erc-4337" rel="noopener noreferrer"&gt;ERC-4337 account abstraction&lt;/a&gt; solved this by putting your account in a smart contract — but that means a &lt;em&gt;new&lt;/em&gt; account at a &lt;em&gt;new address&lt;/em&gt;. Your assets, your token approvals, your history, your ENS name all live at the old EOA. Migration friction is exactly why most users never made the jump.&lt;/p&gt;

&lt;p&gt;EIP-7702 takes the opposite approach: keep your address, keep your key, and let the EOA &lt;em&gt;borrow&lt;/em&gt; a contract's code.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works: the delegation designator
&lt;/h2&gt;

&lt;p&gt;Pectra added a new transaction type, &lt;code&gt;0x04&lt;/code&gt; (the set-code transaction). Alongside the usual fields, it carries an &lt;code&gt;authorizationList&lt;/code&gt;: one or more tuples of &lt;code&gt;{chainId, address, nonce, yParity, r, s}&lt;/code&gt;, each signed by an EOA's key. Each authorization says: "set my account's code to delegate to this contract address."&lt;/p&gt;

&lt;p&gt;When the transaction is processed, the protocol writes a &lt;strong&gt;delegation designator&lt;/strong&gt; into the EOA's code slot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0xef0100 || &amp;lt;20-byte delegate address&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's what the &lt;code&gt;eth_getCode&lt;/code&gt; call above returned: the &lt;code&gt;0xef0100&lt;/code&gt; prefix, then the delegate contract. (The &lt;code&gt;0xef&lt;/code&gt; prefix is reserved by EIP-3541 — no regular deployed contract can start with it, so a designator can never be confused with real bytecode.)&lt;/p&gt;

&lt;p&gt;From that point on, any call or transaction sent &lt;em&gt;to&lt;/em&gt; the EOA executes the delegate contract's code in the EOA's own context — its storage, its balance, its address. The EOA effectively becomes an instance of the delegate contract, while the original private key keeps working for normal transactions too.&lt;/p&gt;

&lt;p&gt;A few mechanics worth knowing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Delegation is persistent.&lt;/strong&gt; It's not per-transaction. It stays until replaced by a new authorization or cleared by authorizing the zero address, which resets the account to a plain EOA.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;chainId&lt;/code&gt; can be 0&lt;/strong&gt;, which makes the authorization valid on every chain — convenient for wallets, but it means one signature can take effect on chains you weren't thinking about.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The authorization embeds the account's nonce&lt;/strong&gt;, so it can't be replayed after the account moves on. If you self-sponsor (the delegating EOA also sends the transaction), sign the authorization with &lt;code&gt;nonce + 1&lt;/code&gt;, because the transaction consumes the current nonce first — the classic off-by-one that ties back to &lt;a href="https://swiftnodes.io/blog/nonce-management-high-throughput-senders" rel="noopener noreferrer"&gt;nonce management&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Each authorization costs gas&lt;/strong&gt; (25,000 for a fresh account), paid by the transaction sender — which can be someone other than the delegating EOA. That's how a wallet with zero ETH gets upgraded: a sponsor submits the type-4 transaction carrying the user's signed authorization.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What it unlocks
&lt;/h2&gt;

&lt;p&gt;Once an EOA delegates to a well-designed smart-account implementation, it gets the ERC-4337 feature set without moving addresses: batch an approve and a swap into a single atomic call, let a dApp or paymaster sponsor gas, hand a game a session key with a spend limit, set up recovery. Major wallets shipped exactly this — MetaMask's smart-account upgrade is a 7702 delegation under the hood, which is why tens of millions of EOAs now return code.&lt;/p&gt;

&lt;p&gt;7702 and ERC-4337 are complements, not competitors. A delegated EOA can point at 4337-compatible account code and then ride the whole 4337 stack — UserOperations, bundlers, paymasters. 7702 fixes the migration problem; 4337 provides the infrastructure.&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;ERC-4337&lt;/th&gt;
&lt;th&gt;EIP-7702&lt;/th&gt;
&lt;th&gt;Native AA (zkSync, Starknet)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Protocol change&lt;/td&gt;
&lt;td&gt;None (contracts + alt mempool)&lt;/td&gt;
&lt;td&gt;New tx type &lt;code&gt;0x04&lt;/code&gt; (Pectra)&lt;/td&gt;
&lt;td&gt;Built into the chain&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Your address&lt;/td&gt;
&lt;td&gt;New contract address&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Same EOA address&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Contract account from day one&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Key still works&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;td&gt;Yes — key retains full control&lt;/td&gt;
&lt;td&gt;Depends on account code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Where it works&lt;/td&gt;
&lt;td&gt;Any EVM chain with bundlers&lt;/td&gt;
&lt;td&gt;Ethereum + L2s that shipped it&lt;/td&gt;
&lt;td&gt;That chain only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What changes when you're building over RPC
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. "No code = EOA" is dead.&lt;/strong&gt; Any classifier, indexer, or security check that calls &lt;code&gt;eth_getCode&lt;/code&gt; and treats a non-empty result as "this is a contract" now misfires on millions of wallets. Check for the designator:&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="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;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="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;mainnet&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/eth?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="nx"&gt;code&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;getCode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045&lt;/span&gt;&lt;span class="dl"&gt;"&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&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="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// plain EOA&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&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;code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0xef0100&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;delegate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`0x&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&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="c1"&gt;// the contract this EOA delegates to&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// regular deployed contract&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Wallets can be callers &lt;em&gt;and&lt;/em&gt; callees.&lt;/strong&gt; A delegated EOA emits events, receives calls, and shows internal-call behavior like a contract, while still originating ordinary transactions with its key. If your indexer keys behavior on "sender must be an EOA" or "recipient with code must be a contract," revisit both branches, and confirm outcomes from &lt;a href="https://swiftnodes.io/blog/reading-transaction-receipts-eth-gettransactionreceipt" rel="noopener noreferrer"&gt;the receipt&lt;/a&gt;, not from account shape.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Type &lt;code&gt;0x04&lt;/code&gt; shows up in your feeds.&lt;/strong&gt; &lt;code&gt;eth_getTransactionByHash&lt;/code&gt; and block bodies now include set-code transactions with an &lt;code&gt;authorizationList&lt;/code&gt; field. If you decode raw transactions yourself (the &lt;a href="https://swiftnodes.io/blog/send-raw-transaction-eth-sendrawtransaction" rel="noopener noreferrer"&gt;eth_sendRawTransaction path&lt;/a&gt;), handle the new type rather than dropping it on the floor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. The security model is sharp-edged.&lt;/strong&gt; The delegate contract controls the account completely — a signature over a malicious delegation is total compromise, which is why "sign this one message" phishing got more dangerous and why wallets only delegate to a shortlist of audited implementations. And the original key always retains full control: 7702 adds capabilities, it does not remove the key. Nothing here changes consensus or the &lt;a href="https://swiftnodes.io/blog/ethereum-mempool-explained" rel="noopener noreferrer"&gt;mempool&lt;/a&gt; — it's account behavior, visible through perfectly ordinary &lt;code&gt;eth_*&lt;/code&gt; calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;EIP-7702 is the quiet one of the big upgrades: no new namespace, no new mempool to integrate, just a 23-byte designator that upgrades the humble EOA in place. If your code touches &lt;code&gt;eth_getCode&lt;/code&gt;, transaction types, or any EOA-versus-contract logic, it's already affected — the one-curl check at the top of this post is the fastest way to see it live.&lt;/p&gt;

&lt;p&gt;You can run that check — and everything else in this post — on any &lt;a href="https://swiftnodes.io/ethereum-rpc" rel="noopener noreferrer"&gt;Ethereum RPC endpoint&lt;/a&gt; from SwiftNodes: flat-rate, HTTP and WebSocket, no KYC. The &lt;a href="https://swiftnodes.io/" rel="noopener noreferrer"&gt;free tier&lt;/a&gt; takes about 30 seconds to set up.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://swiftnodes.io/blog/what-is-eip-7702-eoa-smart-accounts" 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>web3</category>
      <category>blockchain</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Getting Bitcoin Data: Core RPC vs Blockbook vs Hosted Address APIs</title>
      <dc:creator>SwiftNodes</dc:creator>
      <pubDate>Wed, 19 Aug 2026 06:21:01 +0000</pubDate>
      <link>https://dev.to/swiftnodes/getting-bitcoin-data-core-rpc-vs-blockbook-vs-hosted-address-apis-125j</link>
      <guid>https://dev.to/swiftnodes/getting-bitcoin-data-core-rpc-vs-blockbook-vs-hosted-address-apis-125j</guid>
      <description>&lt;p&gt;Developers coming to Bitcoin from Ethereum hit the same wall within an hour. On Ethereum, &lt;code&gt;eth_getBalance(address)&lt;/code&gt; just works. On Bitcoin, you spin up a node, start calling JSON-RPC, and then try to answer "what's the balance of this address?" — and discover that Bitcoin Core simply &lt;em&gt;can't tell you&lt;/em&gt;. There's no &lt;code&gt;getbalanceofaddress&lt;/code&gt;. This isn't a missing feature; it's a consequence of how Bitcoin is built, and it shapes every decision about how you source Bitcoin data. This post covers the gap and the three ways providers fill it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Core RPC can't answer address questions
&lt;/h2&gt;

&lt;p&gt;Bitcoin is a UTXO chain: there's no global map of address → balance. The ledger is a pile of unspent transaction outputs, and an "address balance" is just the sum of the UTXOs currently locked to that address — something nobody maintains as an index unless they choose to build one.&lt;/p&gt;

&lt;p&gt;Bitcoin Core's wallet is also &lt;strong&gt;node-centric&lt;/strong&gt;: &lt;code&gt;getbalance&lt;/code&gt; reports the balance of the &lt;em&gt;wallet the node manages&lt;/em&gt;, not any address you ask about. Even with &lt;code&gt;-txindex&lt;/code&gt; enabled — which lets you fetch any transaction by hash with &lt;code&gt;getrawtransaction&lt;/code&gt; — there's still no way to ask "give me every transaction touching this address" or "list this address's UTXOs." Core indexes transactions, not addresses. (The same is true for Litecoin and Dogecoin, which are Bitcoin forks — see the &lt;a href="https://swiftnodes.io/blog/litecoin-rpc-blockbook-utxo" rel="noopener noreferrer"&gt;Litecoin RPC guide&lt;/a&gt; for the UTXO-specific version of this.)&lt;/p&gt;

&lt;p&gt;So the moment your app needs to show a user their balance, list their coins to build a transaction, or display an address's history, raw Core RPC leaves you stranded. You need an &lt;strong&gt;address index&lt;/strong&gt; layered on top.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three ways to get the data
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Raw Bitcoin Core JSON-RPC
&lt;/h3&gt;

&lt;p&gt;What it's genuinely great at: everything that isn't an address query.&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 &lt;span class="s2"&gt;"https://rpc.swiftnodes.io/rpc/btc?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":"getblockchaininfo","params":[]}'&lt;/span&gt;
&lt;span class="c"&gt;# → { "chain":"main", "blocks":963140, "pruned":false, ... }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Blocks, transactions, mempool inspection, fee estimation, and — critically — &lt;strong&gt;broadcasting&lt;/strong&gt; signed transactions all live here. If you're building a node-adjacent service, Core RPC is the foundation. It just can't do addresses.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Blockbook (the Trezor address indexer)
&lt;/h3&gt;

&lt;p&gt;Blockbook runs alongside &lt;code&gt;bitcoind&lt;/code&gt;, reads the chain, and builds the address index Core lacks. It exposes a REST (and WebSocket) API for exactly the queries you were missing:&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="s2"&gt;"https://rpc.swiftnodes.io/blockbook/btc/api/v2/address/12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX?details=basic&amp;amp;key=YOUR_API_KEY"&lt;/span&gt;
&lt;span class="c"&gt;# → { "address":"12c6DSiU...", "balance":"5135273240", "txs":231, ... }   (balance in satoshis)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's a balance (in satoshis — ~51.35 BTC here), transaction count, and with other endpoints: UTXO lists, full transaction history, and &lt;strong&gt;xpub queries&lt;/strong&gt; that derive and scan an entire HD wallet from an extended public key. Blockbook is what SwiftNodes runs for BTC, LTC, and DOGE, so the same &lt;code&gt;/blockbook/&amp;lt;chain&amp;gt;/api/v2/...&lt;/code&gt; shape works across all three.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Esplora / electrs (the Electrum-family indexers)
&lt;/h3&gt;

&lt;p&gt;Blockstream's Esplora (and the &lt;code&gt;electrs&lt;/code&gt; server behind it) are the other common address-index layer. They solve the same problem with a different API shape — Esplora has its own REST conventions, and the Electrum protocol underpins most light wallets. If you're already in the Electrum ecosystem, this is the natural fit; the trade-off is a different API to learn than Blockbook's.&lt;/p&gt;

&lt;h3&gt;
  
  
  What each actually gives you
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;Core JSON-RPC&lt;/th&gt;
&lt;th&gt;Blockbook&lt;/th&gt;
&lt;th&gt;Esplora/electrs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Blocks, txs, mempool&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;partial&lt;/td&gt;
&lt;td&gt;partial&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Broadcast a signed tx&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fee estimation&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Address balance&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Address UTXOs&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Address tx history&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;xpub / HD-wallet scan&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ (varies)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The pattern is clear: you want &lt;strong&gt;Core RPC for the node operations and an address indexer for everything address-shaped.&lt;/strong&gt; Neither replaces the other.&lt;/p&gt;

&lt;h2&gt;
  
  
  The provider gotcha
&lt;/h2&gt;

&lt;p&gt;Here's where choosing a provider gets tricky. Most RPC providers are &lt;strong&gt;EVM-first&lt;/strong&gt; — Bitcoin is an afterthought, and "Bitcoin support" often means &lt;em&gt;raw Core RPC only&lt;/em&gt;. You can broadcast and read blocks, but the instant you need an address balance you're on your own, because they never built the index.&lt;/p&gt;

&lt;p&gt;So the real question to ask any Bitcoin RPC provider isn't "do you support Bitcoin?" — it's &lt;strong&gt;"do you offer an address / UTXO / xpub API, or just node RPC?"&lt;/strong&gt; The providers that take UTXO seriously (SwiftNodes among them) pair Core JSON-RPC with an address indexer like Blockbook. The ones that don't will quietly leave you to run your own.&lt;/p&gt;

&lt;p&gt;And running your own is not trivial. A full archival &lt;code&gt;bitcoind&lt;/code&gt; plus a Blockbook index is well over 2 TB of fast storage per chain, and the index build is memory-hungry and slow — the kind of thing that's genuinely cheaper to rent than to operate, the same calculus as &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;, with the added twist that you're maintaining &lt;em&gt;two&lt;/em&gt; systems (node + indexer) instead of one. Address queries also need &lt;strong&gt;unpruned&lt;/strong&gt; history, so a pruned node won't do — 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;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to check when picking a Bitcoin RPC provider
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Core JSON-RPC&lt;/strong&gt; for broadcasting, fees, blocks, and mempool.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An address API&lt;/strong&gt; — balance, UTXOs, history, and ideally xpub. This is the piece most providers skip.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The UTXO chains you actually need.&lt;/strong&gt; Many providers do BTC only; if you need Litecoin or Dogecoin too, confirm it. (SwiftNodes runs all three.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Archive, not pruned&lt;/strong&gt; — address history requires full chain data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Predictable pricing&lt;/strong&gt; — the same flat-rate-vs-metered question that applies to &lt;a href="https://swiftnodes.io/blog/reading-balances-eth-getbalance-balanceof" rel="noopener noreferrer"&gt;EVM RPC&lt;/a&gt; applies here.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;Bitcoin Core RPC and an address indexer are two halves of a whole: Core handles blocks, mempool, fees, and broadcasting; Blockbook (or Esplora) handles anything keyed on an address. Raw node access alone can't show a user their balance — and that's the exact spot where a lot of "Bitcoin-supported" providers stop. Pick one that gives you both.&lt;/p&gt;

&lt;p&gt;SwiftNodes serves both halves for BTC, LTC, and DOGE behind one key — Core JSON-RPC at &lt;code&gt;/rpc/&amp;lt;chain&amp;gt;&lt;/code&gt; and the Blockbook address API at &lt;code&gt;/blockbook/&amp;lt;chain&amp;gt;/api/v2/...&lt;/code&gt;, flat-rate with no per-request metering. &lt;a href="https://swiftnodes.io/" rel="noopener noreferrer"&gt;Sign up&lt;/a&gt; for a free key and try an address query against &lt;code&gt;https://rpc.swiftnodes.io/blockbook/btc/api/v2/address/&amp;lt;address&amp;gt;?key=YOUR_API_KEY&lt;/code&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://swiftnodes.io/blog/bitcoin-rpc-address-api-blockbook" 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>bitcoin</category>
      <category>blockchain</category>
      <category>web3</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Sending a Raw Transaction: Why eth_sendTransaction Doesn't Work on a Provider</title>
      <dc:creator>SwiftNodes</dc:creator>
      <pubDate>Tue, 18 Aug 2026 06:00:03 +0000</pubDate>
      <link>https://dev.to/swiftnodes/sending-a-raw-transaction-why-ethsendtransaction-doesnt-work-on-a-provider-3102</link>
      <guid>https://dev.to/swiftnodes/sending-a-raw-transaction-why-ethsendtransaction-doesnt-work-on-a-provider-3102</guid>
      <description>&lt;p&gt;Reading from a chain is easy — &lt;code&gt;eth_call&lt;/code&gt;, &lt;code&gt;eth_getBalance&lt;/code&gt;, done. Writing is where developers get stuck, and it usually starts with the same confusing moment: you try &lt;code&gt;eth_sendTransaction&lt;/code&gt;, and your provider rejects it. Here's what that looks like against a real endpoint:&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;# Ask the node to sign and send for us:&lt;/span&gt;
curl ... &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"method":"eth_sendTransaction","params":[{"from":"0x00..","to":"0x00..","value":"0x0"}]}'&lt;/span&gt;
&lt;span class="c"&gt;# → {"error":{"code":-32000,"message":"unknown account"}}&lt;/span&gt;

&lt;span class="c"&gt;# Ask which accounts the node controls:&lt;/span&gt;
curl ... &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"method":"eth_accounts","params":[]}'&lt;/span&gt;
&lt;span class="c"&gt;# → {"result":[]}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The node controls no accounts, so it can't sign for you. That's not a bug — it's the whole security model. Understanding it is the key to the entire write path.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two methods, and why only one works
&lt;/h2&gt;

&lt;p&gt;There are two ways to submit a transaction, and they differ entirely in &lt;em&gt;who signs&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;eth_sendTransaction&lt;/code&gt;&lt;/strong&gt; asks the &lt;strong&gt;node&lt;/strong&gt; to sign the transaction using a private key in the node's own keystore, for an &lt;em&gt;unlocked&lt;/em&gt; account. This only works if you're running your own node and have loaded your key into it. A shared RPC provider holds no keys — it can't and shouldn't sign on your behalf — so &lt;code&gt;eth_accounts&lt;/code&gt; is empty and &lt;code&gt;eth_sendTransaction&lt;/code&gt; fails with "unknown account."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;eth_sendRawTransaction&lt;/code&gt;&lt;/strong&gt; takes a transaction you've &lt;strong&gt;already signed yourself&lt;/strong&gt;, as a blob of bytes, and broadcasts it. The node never sees your private key — only the signed result. This is how every application that uses a provider sends transactions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the rule is simple: &lt;strong&gt;you sign locally, then send the signed bytes.&lt;/strong&gt; The node is a broadcaster, not a signer. If you send &lt;code&gt;eth_sendRawTransaction&lt;/code&gt; a malformed payload, you get an RLP-decoding error — proof that it expects properly signed, encoded transaction bytes and nothing less.&lt;/p&gt;

&lt;h2&gt;
  
  
  Anatomy of a transaction
&lt;/h2&gt;

&lt;p&gt;Before you can sign one, you assemble it. A modern (EIP-1559, "type 2") transaction has these fields:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;What it is&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nonce&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The sender's transaction count — strict per-account ordering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;to&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Recipient (or omitted for contract creation)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;value&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Wei to send&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Calldata (empty for a plain transfer, or an encoded function call)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gas&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Gas limit — the max units the tx may consume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;maxPriorityFeePerGas&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The tip to the validator&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;maxFeePerGas&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The ceiling you'll pay per gas (base fee + tip)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;chainId&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Which chain this tx is valid on&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Every one of these has to be correct before you sign, because the signature commits to all of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The build → sign → send flow
&lt;/h2&gt;

&lt;p&gt;Five steps, each mapping to an RPC call or a local operation:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Get the nonce.&lt;/strong&gt; &lt;code&gt;eth_getTransactionCount(from, "pending")&lt;/code&gt; gives the next nonce. Under any real send rate this is the single trickiest field — see &lt;a href="https://swiftnodes.io/blog/nonce-management-high-throughput-senders" rel="noopener noreferrer"&gt;nonce management for high-throughput senders&lt;/a&gt; for why "pending" flaps and how to manage nonces locally.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Price it.&lt;/strong&gt; Estimate the gas limit with &lt;code&gt;eth_estimateGas&lt;/code&gt;, and set your fees from the current base fee (via &lt;code&gt;eth_feeHistory&lt;/code&gt; or the latest block's &lt;code&gt;baseFeePerGas&lt;/code&gt;) plus a priority tip. Getting this wrong gets your transaction stuck or overpaying — the mechanics are in &lt;a href="https://swiftnodes.io/blog/estimating-gas-eth-estimategas-eip-1559" rel="noopener noreferrer"&gt;estimating gas right&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set the chain ID.&lt;/strong&gt; &lt;code&gt;eth_chainId&lt;/code&gt;, or a value you already know. This is mandatory (more below).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sign locally.&lt;/strong&gt; Your library signs the assembled fields with your private key and RLP-encodes the result into a &lt;code&gt;0x&lt;/code&gt;-prefixed byte string. This happens entirely in your process — the key never leaves it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Broadcast.&lt;/strong&gt; &lt;code&gt;eth_sendRawTransaction(signedBytes)&lt;/code&gt; returns the transaction hash immediately. That hash means "accepted into the mempool," &lt;strong&gt;not&lt;/strong&gt; "mined" — you still have to wait for a receipt.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then poll &lt;code&gt;eth_getTransactionReceipt(hash)&lt;/code&gt; until it's mined and check &lt;code&gt;status&lt;/code&gt; — because &lt;a href="https://swiftnodes.io/blog/reading-transaction-receipts-eth-gettransactionreceipt" rel="noopener noreferrer"&gt;a mined transaction can still have reverted&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Doing it from a library
&lt;/h2&gt;

&lt;p&gt;Libraries wrap all five steps, but they're all doing the same thing under the hood: sign locally, then call &lt;code&gt;eth_sendRawTransaction&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;viem:&lt;/strong&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;createWalletClient&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;parseEther&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;privateKeyToAccount&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/accounts&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="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;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;privateKeyToAccount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0xYOUR_PRIVATE_KEY&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;wallet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createWalletClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;account&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;mainnet&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/eth?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="c1"&gt;// Fills nonce/gas/fees, signs locally, and broadcasts via eth_sendRawTransaction:&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hash&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;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendTransaction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0xRecipient&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;parseEther&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0.01&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;&lt;strong&gt;ethers v6:&lt;/strong&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;JsonRpcProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Wallet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parseEther&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;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="s2"&gt;https://rpc.swiftnodes.io/rpc/eth?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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wallet&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;Wallet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0xYOUR_PRIVATE_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;provider&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;tx&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;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendTransaction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0xRecipient&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;parseEther&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0.01&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;tx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// waits for the receipt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To see the raw path explicitly — sign, then broadcast the bytes yourself:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signed&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;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signTransaction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0xRecipient&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;parseEther&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0.01&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;nonce&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;getTransactionCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pending&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;chainId&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;gasLimit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;21000&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;maxFeePerGas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="nx"&gt;_000_000_000n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;maxPriorityFeePerGas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;_000_000_000n&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;hash&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;provider&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;eth_sendRawTransaction&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;signed&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="c1"&gt;// the actual broadcast&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;web3.py:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;signed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;w3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign_transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tx_dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;private_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0xYOUR_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;tx_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;w3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_raw_transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;raw_transaction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# calls eth_sendRawTransaction
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Gotchas
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;chainId&lt;/code&gt; is not optional.&lt;/strong&gt; Since EIP-155, the chain ID is baked into the signature for replay protection — a transaction signed for one chain is invalid on another. Omit it or get it wrong and the transaction is rejected. This is also why the same signed transaction can't be replayed across chains.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The returned hash isn't a confirmation.&lt;/strong&gt; &lt;code&gt;eth_sendRawTransaction&lt;/code&gt; returns as soon as the node accepts the transaction into its mempool. It might still be dropped, replaced, or reverted. Treat the hash as a receipt to &lt;em&gt;track&lt;/em&gt;, not proof of success — the &lt;a href="https://swiftnodes.io/blog/ethereum-mempool-explained" rel="noopener noreferrer"&gt;mempool explainer&lt;/a&gt; covers what happens between broadcast and mining.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nonce gaps stall everything.&lt;/strong&gt; Transactions execute in strict nonce order. A missing nonce blocks every higher one behind it until it's filled.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never send your private key to the node.&lt;/strong&gt; The entire point of &lt;code&gt;eth_sendRawTransaction&lt;/code&gt; is that signing happens locally. If a tutorial ever has you putting a private key in an RPC request, stop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Account abstraction changes the path.&lt;/strong&gt; Smart-contract accounts don't originate transactions the EOA way — an ERC-4337 UserOperation goes to a &lt;em&gt;bundler&lt;/em&gt;, not to &lt;code&gt;eth_sendRawTransaction&lt;/code&gt;. See &lt;a href="https://swiftnodes.io/blog/what-is-account-abstraction-erc-4337" rel="noopener noreferrer"&gt;what is account abstraction&lt;/a&gt; for how that submission path differs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;On a provider you always sign locally and broadcast with &lt;code&gt;eth_sendRawTransaction&lt;/code&gt; — the node is a relay, never a keyholder, which is exactly why &lt;code&gt;eth_sendTransaction&lt;/code&gt; returns "unknown account." Assemble the fields, price it, set the chain ID, sign in your own process, send the bytes, then wait for a receipt and check its status. Your library does this for you, but knowing the five steps underneath is what lets you debug a stuck or rejected transaction.&lt;/p&gt;

&lt;p&gt;Need an endpoint to broadcast against? SwiftNodes serves &lt;code&gt;eth_sendRawTransaction&lt;/code&gt; (and the rest of the write path) across 60-plus chains behind one key — &lt;a href="https://swiftnodes.io/" rel="noopener noreferrer"&gt;sign up&lt;/a&gt; and point your wallet client at &lt;code&gt;https://rpc.swiftnodes.io/rpc/eth?key=YOUR_API_KEY&lt;/code&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://swiftnodes.io/blog/send-raw-transaction-eth-sendrawtransaction" 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>How Do Blockchain Bridges Work? Lock-Mint, Burn-Mint, and Who You're Trusting</title>
      <dc:creator>SwiftNodes</dc:creator>
      <pubDate>Mon, 17 Aug 2026 05:32:31 +0000</pubDate>
      <link>https://dev.to/swiftnodes/how-do-blockchain-bridges-work-lock-mint-burn-mint-and-who-youre-trusting-805</link>
      <guid>https://dev.to/swiftnodes/how-do-blockchain-bridges-work-lock-mint-burn-mint-and-who-youre-trusting-805</guid>
      <description>&lt;p&gt;You've used a bridge every time you moved funds onto an L2, and probably several times since without thinking about it. But "bridge" is a single word covering several genuinely different mechanisms, and the differences matter — bridges have been the single largest source of losses in crypto, because the mechanism you pick decides &lt;em&gt;who you're trusting&lt;/em&gt;. This post explains what a bridge actually does, the main designs, and the one question that determines whether a bridge is safe.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why bridging is hard at all
&lt;/h2&gt;

&lt;p&gt;A token on Ethereum can't "move" to another chain. Ethereum has no idea another chain exists, and vice versa — each chain only knows its own state. So an asset never physically travels. Instead, every bridge is some scheme for two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Prove&lt;/strong&gt; that something happened on the source chain (you deposited or locked funds).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Act&lt;/strong&gt; on the destination chain in response (release or mint funds to you).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The entire design space of bridges is different answers to "how do we prove step 1 to chain 2, and who do we trust to do it?" Everything else is detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  The main mechanisms
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Lock-and-mint
&lt;/h3&gt;

&lt;p&gt;The most common third-party design. You lock the real asset in a contract on the source chain, and the bridge mints a &lt;strong&gt;wrapped&lt;/strong&gt; representation on the destination chain. To come back, you burn the wrapped token and the original is unlocked.&lt;/p&gt;

&lt;p&gt;The wrapped token is an IOU: &lt;code&gt;USDC.e&lt;/code&gt; or a bridged &lt;code&gt;WETH&lt;/code&gt; is only worth anything because collateral sits locked on the other side. This is powerful — you can represent any asset anywhere — but it means the wrapped token's value depends entirely on the bridge staying solvent and honest. If the lock contract is drained, every wrapped token it backed becomes worthless.&lt;/p&gt;

&lt;h3&gt;
  
  
  Burn-and-mint
&lt;/h3&gt;

&lt;p&gt;Some assets are issued natively on multiple chains by the same issuer. Here there's no wrapping — you &lt;strong&gt;burn&lt;/strong&gt; the token on the source chain and the issuer &lt;strong&gt;mints&lt;/strong&gt; the equivalent native token on the destination. Circle's cross-chain transfer for USDC works this way, as do "omnichain" token standards. The result is one canonical asset everywhere instead of a patchwork of wrapped IOUs, which avoids the "which bridged USDC is this?" problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Liquidity-pool bridges
&lt;/h3&gt;

&lt;p&gt;The "fast bridge" model (Across, Hop, Stargate, and similar). Instead of minting anything, the bridge keeps liquidity pools on both chains. You deposit on chain A, and a relayer immediately pays you out of the pool on chain B, then the pools rebalance in the background. You get near-instant transfers because nobody waits for a mint — you're just being paid from pre-positioned liquidity. The trade-off is that these bridges are limited by pool depth and charge a fee for the service.&lt;/p&gt;

&lt;h3&gt;
  
  
  Canonical (native) rollup bridges
&lt;/h3&gt;

&lt;p&gt;Every rollup has an &lt;strong&gt;official&lt;/strong&gt; bridge between it and its L1. Depositing from L1 to the L2 is usually fast. Withdrawing back is where the rollup's finality model shows up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Optimistic rollups&lt;/strong&gt; (Arbitrum, Base, Optimism) make you wait a &lt;strong&gt;challenge window&lt;/strong&gt; — around 7 days — before a trustless withdrawal completes, because the system has to allow time for someone to dispute the state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ZK rollups&lt;/strong&gt; (&lt;a href="https://swiftnodes.io/linea-rpc" rel="noopener noreferrer"&gt;Linea&lt;/a&gt;, &lt;a href="https://swiftnodes.io/blog/scroll-rpc-bytecode-equivalent-zkevm" rel="noopener noreferrer"&gt;Scroll&lt;/a&gt;, zkSync) settle via validity proofs, so there's nothing to dispute — withdrawals finalize as soon as the proof is verified on L1, no multi-day window.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That difference is the practical face of &lt;a href="https://swiftnodes.io/blog/l2-finality-soft-vs-hard" rel="noopener noreferrer"&gt;soft vs hard finality&lt;/a&gt;: the canonical bridge is exactly where "the sequencer said so" versus "Ethereum proved it" becomes real money you can or can't withdraw. (This is also why "fast bridges" exist — they front you the liquidity so you don't have to wait out the challenge window.)&lt;/p&gt;

&lt;h2&gt;
  
  
  The question that actually determines safety
&lt;/h2&gt;

&lt;p&gt;Here's the part that matters. All of the above still rests on step 1 — &lt;em&gt;proving to the destination chain that something happened on the source chain&lt;/em&gt;. &lt;strong&gt;How&lt;/strong&gt; that proof is verified is where bridges live or die:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;External validators (multisig / MPC).&lt;/strong&gt; A set of off-chain signers watches the source chain and attests to the destination. Fast and flexible, but you're trusting that signer set. This is the design behind the largest bridge hacks in history — compromise enough keys and you can mint unbacked tokens or drain the lock contract. When you read that a bridge lost hundreds of millions, it was almost always this.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Light-client / native verification.&lt;/strong&gt; The destination chain verifies a cryptographic proof of the source chain's state directly, trusting no external party — only the source chain's own consensus. This is the most secure design and the hardest to build. Some omnichain systems approximate it with a threshold-signature validator set that custodies assets on external chains (&lt;a href="https://swiftnodes.io/blog/zetachain-rpc-omnichain" rel="noopener noreferrer"&gt;ZetaChain's approach to native Bitcoin&lt;/a&gt; is an example of pushing toward native verification of an external chain).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optimistic verification.&lt;/strong&gt; Assume the cross-chain message is valid, but allow a challenge window in which a watcher can prove fraud. Cheaper than full verification, at the cost of a delay.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the useful question when you look at any bridge isn't "how fast is it?" — it's &lt;strong&gt;"who verifies the cross-chain message, and what happens if they're wrong or compromised?"&lt;/strong&gt; Canonical rollup bridges and light-client bridges inherit the security of the chains themselves. External-validator bridges are only as safe as their signer set.&lt;/p&gt;

&lt;h2&gt;
  
  
  The developer's view
&lt;/h2&gt;

&lt;p&gt;If you're building something that reacts to a bridge — crediting a deposit, triggering a downstream action — the mechanics come down to watching events and respecting finality:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Watch the bridge contract's events.&lt;/strong&gt; Deposits and withdrawals emit logs; you track them with &lt;code&gt;eth_getLogs&lt;/code&gt; or a subscription on the bridge contract, the same way you'd index any contract.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never credit a deposit before source-chain finality.&lt;/strong&gt; This is the cardinal rule. If you act on a deposit that later disappears in a reorg, you've paid out real value against nothing. Wait for the source chain's &lt;code&gt;finalized&lt;/code&gt; tag (or enough confirmations), exactly as in &lt;a href="https://swiftnodes.io/blog/handling-chain-reorgs-indexer" rel="noopener noreferrer"&gt;handling chain reorgs&lt;/a&gt;. Reading the deposit's &lt;a href="https://swiftnodes.io/blog/reading-transaction-receipts-eth-gettransactionreceipt" rel="noopener noreferrer"&gt;transaction receipt&lt;/a&gt; confirms it succeeded — but "mined" still isn't "final."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Canonical withdrawals need a proof step.&lt;/strong&gt; Withdrawing from a rollup usually involves submitting a proof on L1 after the challenge/proof window; that's a second transaction on the destination chain, not an automatic credit.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this needs special RPC methods — it's standard &lt;code&gt;eth_*&lt;/code&gt; against both chains, one endpoint per chain.&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;A bridge never moves an asset; it locks or burns on one side and mints or releases on the other, and the only thing standing between you and a loss is &lt;em&gt;how the cross-chain message gets verified&lt;/em&gt;. Wrapped tokens are IOUs backed by locked collateral. Canonical rollup bridges inherit the L2's finality model — instant-ish on ZK, a challenge window on optimistic. And the security question is always the same: who attests that the source-chain event really happened? Answer that, and you understand the bridge.&lt;/p&gt;

&lt;p&gt;Building across chains? SwiftNodes gives you the same endpoint format for 60-plus chains behind one key — &lt;a href="https://swiftnodes.io/" rel="noopener noreferrer"&gt;sign up&lt;/a&gt; and point each client at &lt;code&gt;https://rpc.swiftnodes.io/rpc/&amp;lt;chain&amp;gt;?key=YOUR_API_KEY&lt;/code&gt;, from Ethereum to every L2 you're bridging between.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://swiftnodes.io/blog/how-blockchain-bridges-work" 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>Linea RPC: The zkEVM Where Gas Estimation Works Differently</title>
      <dc:creator>SwiftNodes</dc:creator>
      <pubDate>Sun, 16 Aug 2026 06:16:07 +0000</pubDate>
      <link>https://dev.to/swiftnodes/linea-rpc-the-zkevm-where-gas-estimation-works-differently-b1d</link>
      <guid>https://dev.to/swiftnodes/linea-rpc-the-zkevm-where-gas-estimation-works-differently-b1d</guid>
      <description>&lt;p&gt;Linea is a zkEVM Layer 2 built by Consensys — the team behind MetaMask and Infura — which means two things for developers. First, it's EVM-equivalent, so your existing contracts and tooling connect with a URL change and nothing else. Second, because Consensys ships the wallet most of your users already have, Linea tends to show up as a default network in a lot of places. The RPC surface is standard &lt;code&gt;eth_*&lt;/code&gt;, so the on-ramp is easy.&lt;/p&gt;

&lt;p&gt;The parts worth actually reading about are the two places Linea diverges from a plain Ethereum node: &lt;strong&gt;gas estimation&lt;/strong&gt; and &lt;strong&gt;finality&lt;/strong&gt;. Get those right and everything else is business as usual.&lt;/p&gt;

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

&lt;p&gt;Linea mainnet is EVM chain ID &lt;strong&gt;59144&lt;/strong&gt; (&lt;code&gt;0xe708&lt;/code&gt;). Gas is paid in &lt;strong&gt;ETH&lt;/strong&gt; — no separate native token — so funding, balances, and fee display work exactly like Ethereum. Point any standard client at a SwiftNodes endpoint:&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/linea?key=YOUR_API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;viem&lt;/code&gt;, &lt;code&gt;ethers&lt;/code&gt;, &lt;code&gt;web3.py&lt;/code&gt;, Foundry, and Hardhat all work unchanged:&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="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;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;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/linea?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;// 59144&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;// ~31,700,000 and climbing&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What "zkEVM" means here
&lt;/h2&gt;

&lt;p&gt;Linea is a &lt;strong&gt;zkEVM rollup&lt;/strong&gt;: it executes standard EVM bytecode and proves that execution correct with zero-knowledge validity proofs posted to Ethereum. For a developer, the practical upshot is bytecode-level compatibility — Solidity compiles the same, opcodes behave the same, and audits carry over. The RPC is plain &lt;code&gt;eth_*&lt;/code&gt;; there's no Cairo (like &lt;a href="https://swiftnodes.io/blog/starknet-rpc-non-evm" rel="noopener noreferrer"&gt;Starknet&lt;/a&gt;) or a modified VM to learn.&lt;/p&gt;

&lt;p&gt;It's worth placing Linea against the other ZK L2s we've covered. &lt;a href="https://swiftnodes.io/blog/zksync-era-rpc-differences" rel="noopener noreferrer"&gt;zkSync Era&lt;/a&gt; is EVM-&lt;em&gt;compatible&lt;/em&gt; but not equivalent — it has a different VM, custom account abstraction, and its own transaction type. &lt;a href="https://swiftnodes.io/blog/scroll-rpc-bytecode-equivalent-zkevm" rel="noopener noreferrer"&gt;Scroll&lt;/a&gt; targets bytecode-equivalence. Linea sits in the equivalent camp: standard EVM semantics, standard tooling, with the validity-proof machinery running underneath. In the &lt;a href="https://swiftnodes.io/blog/rollup-vs-validium-vs-optimium" rel="noopener noreferrer"&gt;rollup taxonomy&lt;/a&gt;, it's a ZK rollup — proofs plus data on Ethereum.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gas estimation gotcha
&lt;/h2&gt;

&lt;p&gt;Here's the first place a naive integration goes wrong. On Ethereum you reach for &lt;code&gt;eth_gasPrice&lt;/code&gt; and &lt;code&gt;eth_estimateGas&lt;/code&gt; and move on. On Linea, those don't capture the whole cost, because an L2's fee has a component that reflects the cost of proving and posting data to L1 — and Linea exposes a dedicated method to price it correctly: &lt;strong&gt;&lt;code&gt;linea_estimateGas&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It returns a tuple rather than a single number:&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 &lt;span class="s2"&gt;"https://rpc.swiftnodes.io/rpc/linea?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="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "jsonrpc":"2.0","id":1,"method":"linea_estimateGas",
    "params":[{"from":"0x...","to":"0x...","value":"0x0"}]}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;"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;"0x5208"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"baseFeePerGas"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0x7"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"priorityFeePerGas"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0x24137da"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You get the gas limit, the base fee, and a &lt;strong&gt;recommended priority fee&lt;/strong&gt; in one call. The priority fee it returns is not the near-zero tip you'd assume from mainnet habits — it's computed for Linea's fee market, and using a hand-picked or &lt;code&gt;eth_gasPrice&lt;/code&gt;-derived value instead is the classic way to get transactions that either overpay or sit unmined. If you're building a wallet or a bot on Linea, prefer &lt;code&gt;linea_estimateGas&lt;/code&gt; and use the values it hands back. (It's a Linea-specific method, so this is knowledge that lives with the chain, not with any one provider — the standard &lt;a href="https://swiftnodes.io/blog/estimating-gas-eth-estimategas-eip-1559" rel="noopener noreferrer"&gt;&lt;code&gt;eth_estimateGas&lt;/code&gt; mechanics&lt;/a&gt; still apply for the EVM side.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Finality: the &lt;code&gt;finalized&lt;/code&gt; tag means something specific
&lt;/h2&gt;

&lt;p&gt;The second divergence is finality, and it's a good divergence. Linea produces blocks quickly at the sequencer, but a block is only &lt;em&gt;truly&lt;/em&gt; final once its validity proof is verified on Ethereum. That gives Linea two useful properties compared to optimistic rollups:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No multi-day challenge window.&lt;/strong&gt; An optimistic rollup like Arbitrum or Base makes you wait ~7 days for a trustless L1 withdrawal because someone might dispute the state. A validity proof has nothing to dispute — once it's verified, the state is settled. This is the core ZK-vs-optimistic distinction from &lt;a href="https://swiftnodes.io/blog/l2-finality-soft-vs-hard" rel="noopener noreferrer"&gt;soft vs hard finality&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;code&gt;finalized&lt;/code&gt; block tag is your anchor.&lt;/strong&gt; When you need certainty — bridging value, crediting a deposit, settling something irreversible — read against the &lt;code&gt;finalized&lt;/code&gt; tag rather than &lt;code&gt;latest&lt;/code&gt;, and you're reading L1-proven state.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One caveat that trips people up: the &lt;code&gt;finalized&lt;/code&gt; tag only reflects true L1 finalization on a Linea-aware node. Some generic endpoints alias &lt;code&gt;finalized&lt;/code&gt; to the chain head, which quietly defeats the purpose. If your withdrawal logic depends on real finality, confirm your endpoint reports a &lt;code&gt;finalized&lt;/code&gt; height that &lt;em&gt;lags&lt;/em&gt; &lt;code&gt;latest&lt;/code&gt; — that gap is the proof-verification pipeline, and it's supposed to be there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Indexing and events
&lt;/h2&gt;

&lt;p&gt;Nothing exotic here — &lt;code&gt;eth_getLogs&lt;/code&gt; and log subscriptions work as on any EVM chain. Two habits carry over: respect &lt;code&gt;getLogs&lt;/code&gt; range caps when backfilling, and key your indexed records on &lt;code&gt;(txHash, logIndex)&lt;/code&gt; rather than block number so a reorg near the sequencer head can't corrupt your data. The mechanics are the same as &lt;a href="https://swiftnodes.io/blog/handling-chain-reorgs-indexer" rel="noopener noreferrer"&gt;handling chain reorgs&lt;/a&gt;, and for certainty-sensitive reads, anchor to &lt;code&gt;finalized&lt;/code&gt; as above.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Consensys distribution angle
&lt;/h2&gt;

&lt;p&gt;Linea's quieter advantage is reach. Because Consensys builds MetaMask, Linea is frequently a first-class network in the wallet flows your users already run, which lowers the friction of getting them onto your L2. It doesn't change how you build — the RPC is standard — but it's a real reason Linea shows up in a lot of consumer-facing apps.&lt;/p&gt;

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

&lt;p&gt;Linea is an easy chain to build on and a slightly nuanced one to build &lt;em&gt;well&lt;/em&gt; on. It's an EVM-equivalent zkEVM, so &lt;code&gt;eth_*&lt;/code&gt; and your tooling just work. Two things deserve attention: use &lt;code&gt;linea_estimateGas&lt;/code&gt; for pricing instead of assuming mainnet gas habits, and treat the &lt;code&gt;finalized&lt;/code&gt; tag as your finality anchor — validity proofs mean no 7-day exit window, but only a Linea-aware endpoint reflects real L1 finalization.&lt;/p&gt;

&lt;p&gt;Get a Linea endpoint — and 60-plus other chains behind one consistent URL format — on the &lt;a href="https://swiftnodes.io/linea-rpc" rel="noopener noreferrer"&gt;SwiftNodes Linea RPC page&lt;/a&gt;. There's a free tier to start; &lt;a href="https://swiftnodes.io/" rel="noopener noreferrer"&gt;sign up&lt;/a&gt; and point your client at &lt;code&gt;https://rpc.swiftnodes.io/rpc/linea?key=YOUR_API_KEY&lt;/code&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://swiftnodes.io/blog/linea-rpc-zkevm" 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>eth_call State Overrides: Simulate Against State That Doesn't Exist</title>
      <dc:creator>SwiftNodes</dc:creator>
      <pubDate>Sat, 15 Aug 2026 05:40:41 +0000</pubDate>
      <link>https://dev.to/swiftnodes/ethcall-state-overrides-simulate-against-state-that-doesnt-exist-cf5</link>
      <guid>https://dev.to/swiftnodes/ethcall-state-overrides-simulate-against-state-that-doesnt-exist-cf5</guid>
      <description>&lt;p&gt;Most developers use &lt;code&gt;eth_call&lt;/code&gt; to ask "what does this function return against current state?" But &lt;code&gt;eth_call&lt;/code&gt; has a rarely-used third parameter that answers a much more powerful question: "what would this return if the chain looked &lt;em&gt;different&lt;/em&gt;?" You can hand the node a set of &lt;strong&gt;state overrides&lt;/strong&gt; — fake balances, fake contract code, fake storage slots — and it will run your call against that hypothetical world without touching anything on-chain.&lt;/p&gt;

&lt;p&gt;This is one of the most useful RPC features almost nobody reaches for. It turns a read-only call into a simulator: preview a transaction that would revert today, test a contract patch without deploying it, or read what a contract &lt;em&gt;would&lt;/em&gt; return if an account held tokens it doesn't. This post covers the mechanics, a reproducible example, and the cases where it earns its keep.&lt;/p&gt;

&lt;h2&gt;
  
  
  The third parameter
&lt;/h2&gt;

&lt;p&gt;The full signature is:&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="nf"&gt;eth_call&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt; &lt;span class="nx"&gt;txObject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;blockParameter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stateOverrideSet&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;stateOverrideSet&lt;/code&gt; is an object keyed by address, where each entry can override:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Effect&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;balance&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set the account's ETH balance (hex wei)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nonce&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set the account's nonce&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;code&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Replace the account's bytecode with your own&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;state&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Replace the account's &lt;strong&gt;entire&lt;/strong&gt; storage (all other slots read as zero)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;stateDiff&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Patch &lt;strong&gt;specific&lt;/strong&gt; storage slots, leaving the rest untouched&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Nothing is persisted — the override exists only for the duration of that single call. The node builds a temporary view of state, runs your call against it, and throws the view away.&lt;/p&gt;

&lt;h2&gt;
  
  
  A reproducible example
&lt;/h2&gt;

&lt;p&gt;Here's the smallest demonstration. We call an empty address, but override its &lt;code&gt;code&lt;/code&gt; with runtime bytecode that always returns the number 42 (&lt;code&gt;0x2a&lt;/code&gt;). The bytecode &lt;code&gt;0x602a60005260206000f3&lt;/code&gt; is just: push &lt;code&gt;0x2a&lt;/code&gt;, store it in memory, return 32 bytes.&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 &lt;span class="s2"&gt;"https://rpc.swiftnodes.io/rpc/eth?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="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "jsonrpc":"2.0","id":1,"method":"eth_call","params":[
      {"to":"0x0000000000000000000000000000000000000abc","data":"0x"},
      "latest",
      {"0x0000000000000000000000000000000000000abc":{"code":"0x602a60005260206000f3"}}
    ]}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0x000000000000000000000000000000000000000000000000000000000000002a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the same call without the override and you get &lt;code&gt;0x&lt;/code&gt; — an empty account with no code returns nothing. The override materialized a contract that never existed on-chain, ran your call against it, and returned 42.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this is actually useful
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Simulate a transaction for an account that can't afford it yet.&lt;/strong&gt; Testing a swap or a contract interaction usually fails if the caller doesn't hold the tokens or ETH. Override the caller's &lt;code&gt;balance&lt;/code&gt; and you can preview whether the call &lt;em&gt;would&lt;/em&gt; succeed with funds, before the user has deposited anything. This pairs naturally with gas estimation — &lt;code&gt;eth_estimateGas&lt;/code&gt; accepts the same override set, so you can estimate gas for a transaction that would revert against real state (see &lt;a href="https://swiftnodes.io/blog/estimating-gas-eth-estimategas-eip-1559" rel="noopener noreferrer"&gt;estimating gas right&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Preview a token operation without an approval.&lt;/strong&gt; ERC-20 transfers through a router need an allowance, which is a real on-chain transaction. With a &lt;code&gt;stateDiff&lt;/code&gt;, you can set the allowance storage slot to a large value and simulate the whole swap as if the approval already happened — useful for quoting an exact-output trade before asking the user to sign anything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test a contract change without deploying it.&lt;/strong&gt; Override the &lt;code&gt;code&lt;/code&gt; at an existing contract's address with a patched version and call it. You get to see how the new logic behaves against live storage and live surrounding contracts, with zero deployment cost. This is how a lot of "what if we fixed this bug" analysis gets done.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Read a contract under hypothetical storage.&lt;/strong&gt; Override a storage slot with &lt;code&gt;stateDiff&lt;/code&gt; to flip a boolean (say, a &lt;code&gt;paused&lt;/code&gt; flag) or change an &lt;code&gt;owner&lt;/code&gt;, then call a function gated on it. You're asking "what would this return if the contract were in that state?" without waiting for it to actually get there.&lt;/p&gt;

&lt;h2&gt;
  
  
  state vs stateDiff — use stateDiff
&lt;/h2&gt;

&lt;p&gt;The difference between &lt;code&gt;state&lt;/code&gt; and &lt;code&gt;stateDiff&lt;/code&gt; trips people up and matters a lot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;state&lt;/code&gt;&lt;/strong&gt; replaces the account's &lt;em&gt;entire&lt;/em&gt; storage. Every slot you didn't specify reads back as zero. If the contract depends on any other storage (almost all do), it will misbehave.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;stateDiff&lt;/code&gt;&lt;/strong&gt; patches only the slots you list and leaves everything else intact.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In practice you almost always want &lt;code&gt;stateDiff&lt;/code&gt;. Reach for &lt;code&gt;state&lt;/code&gt; only when you genuinely want a blank-slate storage.&lt;/p&gt;

&lt;p&gt;Finding the &lt;em&gt;right&lt;/em&gt; slot is the hard part. For a mapping like &lt;code&gt;mapping(address =&amp;gt; uint256) balances&lt;/code&gt; declared at slot &lt;code&gt;p&lt;/code&gt;, the slot for key &lt;code&gt;k&lt;/code&gt; is &lt;code&gt;keccak256(abi.encode(k, p))&lt;/code&gt;. Fixed variables occupy sequential slots from 0. Getting the layout right is the main friction of storage overrides — tooling like Foundry's storage layout output helps, and the &lt;a href="https://swiftnodes.io/blog/reading-balances-eth-getbalance-balanceof" rel="noopener noreferrer"&gt;reading balances&lt;/a&gt; post covers why you should read storage rather than reconstruct it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Doing it from a library
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;viem&lt;/strong&gt; has first-class support via &lt;code&gt;stateOverride&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&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;call&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0x0000000000000000000000000000000000000abc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;data&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="na"&gt;stateOverride&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="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0x0000000000000000000000000000000000000abc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0x602a60005260206000f3&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;web3.py&lt;/strong&gt; takes a &lt;code&gt;state_override&lt;/code&gt; argument:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;w3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0x0000000000000000000000000000000000000abc&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0x&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;latest&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;state_override&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0x0000000000000000000000000000000000000abc&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;code&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0x602a60005260206000f3&lt;/span&gt;&lt;span class="sh"&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;&lt;strong&gt;ethers v6&lt;/strong&gt; doesn't expose overrides on &lt;code&gt;provider.call&lt;/code&gt;, so send the raw request — the third array element is the override set:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&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;provider&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;eth_call&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="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0x0000000000000000000000000000000000000abc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;data&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="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="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0x0000000000000000000000000000000000000abc&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;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0x602a60005260206000f3&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Gotchas
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Not every endpoint supports it.&lt;/strong&gt; State overrides are a client feature (Geth, Erigon, Reth, Nethermind implement them), but many shared public endpoints strip the third parameter or reject it. If your override seems ignored — the call returns the un-overridden result — you're likely on an endpoint that dropped it. SwiftNodes passes the override through to a supporting node; the example above runs against our Ethereum endpoint as-is.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's a simulation, full stop.&lt;/strong&gt; Nothing is written. Two calls with different overrides don't interact.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Combine with a historical block.&lt;/strong&gt; The block parameter still applies, so you can override state &lt;em&gt;and&lt;/em&gt; pin to an old block — "what would this have returned at block N if the account had held tokens?" — which requires an archive node (&lt;a href="https://swiftnodes.io/docs/api-reference#archive" rel="noopener noreferrer"&gt;archive access via &lt;code&gt;&amp;amp;archive=1&lt;/code&gt;&lt;/a&gt;, and see &lt;a href="https://swiftnodes.io/blog/eth-call-historical-block" rel="noopener noreferrer"&gt;reading historical state&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tracing accepts overrides too.&lt;/strong&gt; &lt;code&gt;debug_traceCall&lt;/code&gt; takes the same override set, so you can get a full execution trace of your simulated call, not just its return value.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;eth_call&lt;/code&gt; isn't only a way to read the chain as it is — with the third parameter it's a way to read the chain as it &lt;em&gt;might be&lt;/em&gt;. Override a balance to preview a funded transaction, override code to test a fix, override a storage slot to explore a hypothetical, and estimate gas for calls that would revert today. It's a simulator hiding inside a method you already use.&lt;/p&gt;

&lt;p&gt;Want a node that actually honors the third parameter? SwiftNodes forwards &lt;code&gt;eth_call&lt;/code&gt; overrides straight through to a supporting Ethereum node — &lt;a href="https://swiftnodes.io/" rel="noopener noreferrer"&gt;sign up&lt;/a&gt; for a free key and point your client at &lt;code&gt;https://rpc.swiftnodes.io/rpc/eth?key=YOUR_API_KEY&lt;/code&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://swiftnodes.io/blog/eth-call-state-overrides" 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>Ankr Alternative: Public Endpoints, API Credits, and the Flat-Rate Option</title>
      <dc:creator>SwiftNodes</dc:creator>
      <pubDate>Fri, 14 Aug 2026 05:43:30 +0000</pubDate>
      <link>https://dev.to/swiftnodes/ankr-alternative-public-endpoints-api-credits-and-the-flat-rate-option-3o8c</link>
      <guid>https://dev.to/swiftnodes/ankr-alternative-public-endpoints-api-credits-and-the-flat-rate-option-3o8c</guid>
      <description>&lt;p&gt;You have probably used Ankr without deciding to. &lt;code&gt;https://rpc.ankr.com/eth&lt;/code&gt; is pasted into more MetaMask networks, tutorials, and quick scripts than almost any other endpoint, because it's free, needs no API key, and covers 80-plus chains. As a way to get a read call working in thirty seconds, it's genuinely excellent.&lt;/p&gt;

&lt;p&gt;The trouble starts when a prototype becomes a product. The free public endpoint is deliberately limited, and the paid path replaces those limits with a metered &lt;strong&gt;API-credit&lt;/strong&gt; model that brings back the exact budgeting problem developers move to flat-rate providers to escape. This post walks through both walls and where an alternative like SwiftNodes fits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wall one: the public endpoint is capped by design
&lt;/h2&gt;

&lt;p&gt;Ankr's public endpoints are anonymous and free, which means they have to be defended against abuse. In practice that means three restrictions that are invisible in development and painful in production:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A global, IP-level rate throttle.&lt;/strong&gt; There's no per-key budget you can reason about — you share a throttle with everyone else hitting that public URL from your network. Under real traffic you get intermittent rate-limit errors you can't predict or raise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A restricted method set.&lt;/strong&gt; The public tier doesn't serve archive queries, &lt;code&gt;debug_*&lt;/code&gt;/trace methods, or wide &lt;code&gt;eth_getLogs&lt;/code&gt; ranges. Those are exactly the calls that indexers, analytics, and anything reading historical state depend on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No private endpoint.&lt;/strong&gt; You can't get a stable, isolated endpoint with its own limits without moving to a paid plan.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first time this usually bites is indexing. You write a backfill that pages &lt;code&gt;eth_getLogs&lt;/code&gt;, it works against a small range in dev, and then it fails in production because the public endpoint caps the range — the same class of problem covered in &lt;a href="https://swiftnodes.io/blog/eth-getlogs-range-caps" rel="noopener noreferrer"&gt;how &lt;code&gt;eth_getLogs&lt;/code&gt; range caps bite you&lt;/a&gt;. The second time is when you need a historical &lt;code&gt;eth_call&lt;/code&gt; or a &lt;code&gt;debug_traceTransaction&lt;/code&gt; for accounting, and discover the public tier simply doesn't answer those (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 why archive access is a separate capability, not a setting).&lt;/p&gt;

&lt;p&gt;None of this is a knock on Ankr — free anonymous infrastructure &lt;em&gt;has&lt;/em&gt; to be limited. It just means the public endpoint is a starting line, not a finish line.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wall two: paid means API credits, priced per method
&lt;/h2&gt;

&lt;p&gt;When you outgrow public, Ankr's paid path is &lt;strong&gt;pay-as-you-go API credits&lt;/strong&gt;. The headline number is simple — roughly &lt;strong&gt;$0.10 per 1M credits&lt;/strong&gt; (starting around $10 per 100M) — but the catch is that &lt;em&gt;different methods cost different amounts of credits&lt;/em&gt;. An &lt;code&gt;eth_call&lt;/code&gt; runs about 200 credits; heavier methods cost more. So your real cost per request depends entirely on your method mix.&lt;/p&gt;

&lt;p&gt;Work an example. If your workload is mostly &lt;code&gt;eth_call&lt;/code&gt; at ~200 credits each:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1,000,000 eth_call  ×  200 credits  =  200,000,000 credits
200,000,000 credits  ×  $0.10 / 1,000,000  =  $20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That looks cheap until you remember two things. First, real apps aren't one method — they mix &lt;code&gt;getLogs&lt;/code&gt;, &lt;code&gt;getBlockByNumber&lt;/code&gt;, traces, and receipts, each at a different credit weight, so you can't predict the bill without modeling the whole distribution. Second, that distribution &lt;em&gt;changes&lt;/em&gt; the moment you ship a new feature or traffic spikes. This is the same metered-compute trap we've written about for &lt;a href="https://swiftnodes.io/blog/alchemy-compute-units" rel="noopener noreferrer"&gt;Alchemy's compute units&lt;/a&gt; and &lt;a href="https://swiftnodes.io/blog/quicknode-api-credits" rel="noopener noreferrer"&gt;QuickNode's credits&lt;/a&gt;: the pricing unit isn't a request, it's a synthetic cost that you have to translate back into requests every time you want to answer "what will this cost me next month?"&lt;/p&gt;

&lt;p&gt;Ankr's Freemium tier softens the entry — a monthly allotment of free credits — but it still runs under the public rate limits, so it's a bigger evaluation bucket, not a production lane.&lt;/p&gt;

&lt;h2&gt;
  
  
  What flat-rate changes
&lt;/h2&gt;

&lt;p&gt;The alternative model is to pay one number per month and stop counting. That's what SwiftNodes does, and the difference isn't philosophical — it removes concrete work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No method-weight modeling.&lt;/strong&gt; A request is a request. An &lt;code&gt;eth_call&lt;/code&gt;, an &lt;code&gt;eth_getLogs&lt;/code&gt;, and a &lt;code&gt;debug_traceTransaction&lt;/code&gt; all draw from the same flat plan, so you never build a spreadsheet to predict a bill. For trace-heavy or log-heavy workloads — the ones Ankr's credit weights punish hardest — this is the biggest saving.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Archive and full methods on paid plans.&lt;/strong&gt; The methods the public tier withholds — archive state, &lt;code&gt;debug_*&lt;/code&gt;/trace, wide &lt;code&gt;getLogs&lt;/code&gt; — are available, so you're not forced onto pay-as-you-go just to read history.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One URL format across every chain.&lt;/strong&gt; &lt;code&gt;https://rpc.swiftnodes.io/rpc/eth?key=YOUR_API_KEY&lt;/code&gt; (and &lt;code&gt;/ws/eth&lt;/code&gt; for WebSocket). The slug is the only thing that changes between chains — see the &lt;a href="https://swiftnodes.io/ethereum-rpc" rel="noopener noreferrer"&gt;Ethereum RPC page&lt;/a&gt; for a live example.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's the shape of the trade-off:&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;Ankr Public&lt;/th&gt;
&lt;th&gt;Ankr Premium (credits)&lt;/th&gt;
&lt;th&gt;SwiftNodes (flat-rate)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;API key&lt;/td&gt;
&lt;td&gt;none&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;Rate limit&lt;/td&gt;
&lt;td&gt;shared IP throttle&lt;/td&gt;
&lt;td&gt;high, per plan&lt;/td&gt;
&lt;td&gt;fixed req/s per plan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost model&lt;/td&gt;
&lt;td&gt;free&lt;/td&gt;
&lt;td&gt;credits, per-method weight&lt;/td&gt;
&lt;td&gt;flat monthly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Predict next month's bill&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;td&gt;model your method mix&lt;/td&gt;
&lt;td&gt;it's the plan price&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Archive / debug / wide getLogs&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes (paid plans)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chains&lt;/td&gt;
&lt;td&gt;80+ public&lt;/td&gt;
&lt;td&gt;80+&lt;/td&gt;
&lt;td&gt;60+ behind one key&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For the broader "free public RPC vs a paid endpoint" decision — latency, reliability, and where the line actually is — &lt;a href="https://swiftnodes.io/blog/public-rpc-vs-paid-rpc" rel="noopener noreferrer"&gt;public RPC vs paid RPC&lt;/a&gt; goes deeper, and if you're weighing running your own node instead, &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; has the cost math.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Ankr is the right call
&lt;/h2&gt;

&lt;p&gt;Flat-rate isn't automatically the answer, and it's worth being honest about where Ankr wins:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prototyping and low-volume read apps.&lt;/strong&gt; If you never leave read-only, low-traffic territory, the public endpoints are free and cover more chains anonymously than anyone. That's hard to beat for a hackathon or a hobby project.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You want anonymous, no-signup access.&lt;/strong&gt; No key, no account — paste and go.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You're already in the Ankr ecosystem&lt;/strong&gt; — its staking, RaaS (rollup-as-a-service), or enterprise node products. Keeping RPC in the same account has real convenience value.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your method mix is genuinely cheap and stable.&lt;/strong&gt; If you're overwhelmingly light &lt;code&gt;eth_call&lt;/code&gt; traffic and you've actually modeled it, pay-as-you-go credits can come out inexpensive.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The point isn't that credits are bad — it's that credits are &lt;em&gt;work&lt;/em&gt;. If you'd rather not re-model your bill every time your traffic changes, flat-rate is the trade you want.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migration is a URL swap
&lt;/h2&gt;

&lt;p&gt;Because both speak standard JSON-RPC, moving is mechanical. Point your client at the new endpoint and nothing else changes:&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="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;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;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/eth?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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same &lt;code&gt;eth_*&lt;/code&gt; methods, same responses — you're just trading a shared public throttle (or a per-method credit meter) for a single predictable monthly number, with archive and trace access included on paid plans.&lt;/p&gt;

&lt;p&gt;If Ankr's public endpoints have gotten you this far and you're starting to hit the walls above, that's the signal you've outgrown the starting line. SwiftNodes has a free tier to test with — &lt;a href="https://swiftnodes.io/" rel="noopener noreferrer"&gt;sign up&lt;/a&gt;, point your client at &lt;code&gt;/rpc/&amp;lt;chain&amp;gt;?key=YOUR_API_KEY&lt;/code&gt;, and see whether flat-rate fits your workload better than counting credits.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://swiftnodes.io/blog/ankr-alternative-public-rpc-credits" 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>Robinhood Chain RPC: Tokenized Stocks on an Arbitrum Orbit L2</title>
      <dc:creator>SwiftNodes</dc:creator>
      <pubDate>Thu, 13 Aug 2026 07:37:05 +0000</pubDate>
      <link>https://dev.to/swiftnodes/robinhood-chain-rpc-tokenized-stocks-on-an-arbitrum-orbit-l2-d33</link>
      <guid>https://dev.to/swiftnodes/robinhood-chain-rpc-tokenized-stocks-on-an-arbitrum-orbit-l2-d33</guid>
      <description>&lt;p&gt;Robinhood Chain went live on mainnet on July 1, 2026, and it arrived with a specific job: settle &lt;strong&gt;tokenized real-world assets&lt;/strong&gt; — most visibly Robinhood's tokenized stock tokens. That framing scares off some developers who assume an "RWA chain" needs a specialized SDK and a compliance degree. It doesn't. Under the hood, Robinhood Chain is an &lt;strong&gt;Arbitrum Orbit&lt;/strong&gt; rollup running the same Nitro stack as Arbitrum One, which means the RPC surface is plain, boring, wonderful &lt;code&gt;eth_*&lt;/code&gt;. Your existing tooling connects with a URL change and nothing else.&lt;/p&gt;

&lt;p&gt;The interesting part isn't the transport — it's what those tokens &lt;em&gt;do&lt;/em&gt; when you touch them. This post covers connecting, what carries over unchanged from Ethereum, and the one place a tokenized-asset chain will bite you if you treat its tokens like any other ERC-20.&lt;/p&gt;

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

&lt;p&gt;Robinhood Chain is EVM chain ID &lt;strong&gt;4663&lt;/strong&gt; (&lt;code&gt;0x1237&lt;/code&gt;). Point any standard client at a SwiftNodes endpoint:&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/robinhood?key=YOUR_API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because it's a Nitro chain, &lt;code&gt;viem&lt;/code&gt;, &lt;code&gt;ethers&lt;/code&gt;, &lt;code&gt;web3.py&lt;/code&gt;, Foundry, and Hardhat all work with zero changes:&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="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;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;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/robinhood?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;// 4663&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;// ~35,190,000 and climbing&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Gas is paid in &lt;strong&gt;ETH&lt;/strong&gt; — there is no separate native "Robinhood" gas token, which is a deliberate choice that keeps funding, balance-reads, and fee display identical to Ethereum. Fees run in fractions of a gwei, as you'd expect from an Orbit L2. If you've integrated Arbitrum before, you already know how to integrate this chain.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "Arbitrum Orbit" buys you
&lt;/h2&gt;

&lt;p&gt;Orbit is Arbitrum's framework for launching an L2/L3 on the Nitro codebase. For a developer, three things follow from that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;EVM equivalence.&lt;/strong&gt; Contracts, opcodes, and the JSON-RPC method set match Ethereum. There's no custom namespace to learn (contrast that with &lt;a href="https://swiftnodes.io/blog/starknet-rpc-non-evm" rel="noopener noreferrer"&gt;Starknet's &lt;code&gt;starknet_*&lt;/code&gt;&lt;/a&gt; or &lt;a href="https://swiftnodes.io/blog/cosmos-rpc-explained" rel="noopener noreferrer"&gt;Cosmos-based chains&lt;/a&gt;, where &lt;code&gt;eth_*&lt;/code&gt; doesn't exist at all).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A settlement path.&lt;/strong&gt; Robinhood Chain doesn't finalize on its own island. Like other rollups, it produces fast soft confirmations from its sequencer, then settles batches down the Arbitrum/Ethereum path. If you're fuzzy on why "the sequencer confirmed it" and "it's final" are different claims, the &lt;a href="https://swiftnodes.io/blog/l2-finality-soft-vs-hard" rel="noopener noreferrer"&gt;soft vs hard finality&lt;/a&gt; post is the one to read, along with &lt;a href="https://swiftnodes.io/blog/what-is-a-sequencer" rel="noopener noreferrer"&gt;what a sequencer actually is&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ethereum-grade tooling.&lt;/strong&gt; Block explorers, indexers, and wallets that speak generic EVM will work here without special cases.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Practically: build against Robinhood Chain the way you'd build against &lt;a href="https://swiftnodes.io/arbitrum-rpc" rel="noopener noreferrer"&gt;Arbitrum&lt;/a&gt; or any other Orbit chain. For confirmation semantics, treat soft confirmations as "probably done, good enough for UX" and wait for settlement when you're moving value that can't be reversed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one thing that isn't standard: the assets themselves
&lt;/h2&gt;

&lt;p&gt;Here's the part specific to a tokenized-asset chain. The &lt;em&gt;chain&lt;/em&gt; is standard EVM. The &lt;strong&gt;tokens&lt;/strong&gt; representing stocks and real-world assets frequently are not plain ERC-20s, and that gap is where naive integrations break.&lt;/p&gt;

&lt;p&gt;A vanilla ERC-20 lets anyone hold and anyone transfer. A tokenized equity can't work that way — the issuer has regulatory obligations about &lt;em&gt;who&lt;/em&gt; is allowed to hold the asset and under &lt;em&gt;what&lt;/em&gt; conditions it can move. So RWA and security tokens commonly layer compliance logic on top of the ERC-20 interface: allowlists (only KYC'd/whitelisted addresses can receive), transfer restrictions (blackout windows, jurisdiction checks), forced-transfer or freeze hooks for the issuer, and sometimes a partitioned-balance model in the style of ERC-1400. From the RPC's point of view all of this is invisible — you still call &lt;code&gt;balanceOf&lt;/code&gt; and &lt;code&gt;transfer&lt;/code&gt; — but the &lt;em&gt;behavior&lt;/em&gt; differs.&lt;/p&gt;

&lt;p&gt;The failure mode is concrete: &lt;strong&gt;a transfer that a wallet UI happily builds can revert on-chain&lt;/strong&gt; because the recipient isn't eligible, or the transfer window is closed, or a compliance module said no. If your code assumes a transfer that lands in a block succeeded, you'll report success on a transaction that actually reverted. It won't. Two habits protect you:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check the receipt status, don't assume.&lt;/strong&gt; A mined transaction can still have &lt;code&gt;status: 0x0&lt;/code&gt; (reverted) while consuming gas. Read the receipt and branch on status — this is exactly the &lt;a href="https://swiftnodes.io/blog/reading-transaction-receipts-eth-gettransactionreceipt" rel="noopener noreferrer"&gt;mined ≠ succeeded&lt;/a&gt; trap, and it matters far more on a compliance-gated token than on a plain one.
&lt;/li&gt;
&lt;/ol&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;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;waitForTransactionReceipt&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;hash&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;receipt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;success&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="c1"&gt;// transfer reverted — likely a compliance/eligibility check, not a gas issue&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Simulate before you send.&lt;/strong&gt; Use &lt;code&gt;eth_call&lt;/code&gt; (or &lt;code&gt;client.simulateContract&lt;/code&gt;) against the token to see whether the transfer would revert &lt;em&gt;before&lt;/em&gt; you spend gas and show the user a spinner. Many compliance tokens also expose a "can this address receive?" view function — call it and gate your UI on the answer rather than discovering the rule after the fact.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;None of this requires a special RPC. It requires treating "is this an ERC-20?" as a question with a nuanced answer on an RWA chain, and reading contract state instead of assuming the standard interface implies standard behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading balances and prices correctly
&lt;/h2&gt;

&lt;p&gt;Two more habits carry over from general EVM work but are worth repeating because RWA tokens make the stakes higher:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Don't assume 18 decimals.&lt;/strong&gt; Read &lt;code&gt;decimals()&lt;/code&gt; per token and cache it. Tokenized assets may use their own precision, and a display bug on a stock token is more embarrassing than a display bug on a memecoin. The full set of balance traps — decimals, snapshotting at a block, not reconstructing balances from &lt;code&gt;Transfer&lt;/code&gt; events — is in &lt;a href="https://swiftnodes.io/blog/reading-balances-eth-getbalance-balanceof" rel="noopener noreferrer"&gt;reading balances right&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pin reads to a block.&lt;/strong&gt; When you show a portfolio, read every balance at the same &lt;code&gt;blockNumber&lt;/code&gt; so the snapshot is internally consistent. Historical reads (what did this address hold last Tuesday?) need archive state — see the availability note below.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Indexing and event streams
&lt;/h2&gt;

&lt;p&gt;Robinhood Chain produces blocks quickly, so if you're tracking token transfers or issuance events, prefer streaming over tight polling where you can. Standard &lt;code&gt;eth_getLogs&lt;/code&gt; and log subscriptions apply; filter on the token's &lt;code&gt;Transfer&lt;/code&gt; topic and key your records on &lt;code&gt;(txHash, logIndex)&lt;/code&gt; rather than block number so a reorg near the tip doesn't corrupt your index. That reorg-safe indexing pattern is covered in &lt;a href="https://swiftnodes.io/blog/handling-chain-reorgs-indexer" rel="noopener noreferrer"&gt;handling chain reorgs&lt;/a&gt;, and the mechanics of decoding those logs (topics, signatures, indexed params) are in &lt;a href="https://swiftnodes.io/blog/decoding-event-logs-topics-signatures" rel="noopener noreferrer"&gt;decoding event logs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest notes on a young chain
&lt;/h2&gt;

&lt;p&gt;Robinhood Chain launched in mid-2026, so calibrate expectations accordingly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Archive state and WebSockets are still maturing.&lt;/strong&gt; Deep historical queries (&lt;code&gt;eth_getBalance&lt;/code&gt;/&lt;code&gt;eth_call&lt;/code&gt; at old blocks, traces over history) depend on archive nodes being available, and not every endpoint on a new chain exposes them yet. If you need guaranteed archive access, request it explicitly — on SwiftNodes that's the &lt;code&gt;&amp;amp;archive=1&lt;/code&gt; flag, which routes only to archive-capable upstreams and returns a clear error rather than silently serving you a pruned node.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The ecosystem is early.&lt;/strong&gt; A growing DeFi footprint (Arcus, Uniswap, Lighter) is forming around the chain, but tooling coverage will lag more established L2s for a while. Test against the live chain rather than trusting third-party assumptions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify contract behavior per asset.&lt;/strong&gt; As above — the compliance model can differ from token to token. Read the contract, don't generalize.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Robinhood Chain is easy to integrate and easy to get subtly wrong. It's an Arbitrum Orbit L2, so connecting is a one-line URL change and every &lt;code&gt;eth_*&lt;/code&gt; method you know works. The nuance lives in the assets: tokenized stocks and RWAs carry compliance logic that can make a well-formed transfer revert, so check receipts, simulate first, and read contract state instead of assuming the plain ERC-20 contract implies plain ERC-20 behavior.&lt;/p&gt;

&lt;p&gt;Get a Robinhood Chain endpoint — and 60-plus other chains behind one consistent URL format — on the &lt;a href="https://swiftnodes.io/robinhood-chain-rpc" rel="noopener noreferrer"&gt;SwiftNodes Robinhood Chain page&lt;/a&gt;. There's a free tier to start; &lt;a href="https://swiftnodes.io/" rel="noopener noreferrer"&gt;sign up&lt;/a&gt; and point your client at &lt;code&gt;https://rpc.swiftnodes.io/rpc/robinhood?key=YOUR_API_KEY&lt;/code&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://swiftnodes.io/blog/robinhood-chain-rpc-tokenized-stocks" 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>Scroll RPC: The zkEVM That Runs Ethereum Bytecode Unchanged</title>
      <dc:creator>SwiftNodes</dc:creator>
      <pubDate>Wed, 12 Aug 2026 05:07:09 +0000</pubDate>
      <link>https://dev.to/swiftnodes/scroll-rpc-the-zkevm-that-runs-ethereum-bytecode-unchanged-36cp</link>
      <guid>https://dev.to/swiftnodes/scroll-rpc-the-zkevm-that-runs-ethereum-bytecode-unchanged-36cp</guid>
      <description>&lt;p&gt;"zkEVM" covers a wide spectrum, and where a chain sits on it decides how much of your Ethereum knowledge transfers. Scroll is at the far end: it's a &lt;strong&gt;bytecode-equivalent zkEVM&lt;/strong&gt; (chain ID &lt;strong&gt;534352&lt;/strong&gt;), meaning it targets &lt;em&gt;opcode-level&lt;/em&gt; equality with the EVM. Your existing contracts deploy with no changes, the same audits still apply, the same tooling works, and the RPC is plain &lt;code&gt;eth_*&lt;/code&gt; — while ZK validity proofs give it &lt;strong&gt;~1-hour hard finality&lt;/strong&gt; instead of an optimistic rollup's 7-day withdrawal window. If you've read our &lt;a href="https://swiftnodes.io/blog/zksync-era-rpc-differences" rel="noopener noreferrer"&gt;zkSync Era spotlight&lt;/a&gt;, Scroll is the interesting contrast: where zkSync is &lt;em&gt;EVM-compatible&lt;/em&gt; (Solidity works, but it's a different VM under the hood), Scroll aims to be &lt;em&gt;EVM-equivalent&lt;/em&gt;. Here's the map.&lt;/p&gt;

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

&lt;p&gt;Scroll mainnet (live since October 2023) is &lt;strong&gt;chain ID 534352&lt;/strong&gt;, a ZK rollup with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ETH as the gas token&lt;/strong&gt; (18 decimals) — no separate token to hold.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;~3-second blocks&lt;/strong&gt; — fast soft confirmation from the sequencer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bytecode-equivalent zkEVM&lt;/strong&gt; — full opcode equivalence with the EVM; existing Ethereum contracts run identically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ZK validity proofs&lt;/strong&gt; — every state transition is proven and verified on Ethereum, giving &lt;strong&gt;hard finality in ~1 hour&lt;/strong&gt; (no fraud-proof challenge window).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A true rollup&lt;/strong&gt; — transaction data is posted to Ethereum (via &lt;a href="https://swiftnodes.io/blog/what-are-blobs-eip-4844" rel="noopener noreferrer"&gt;blobs&lt;/a&gt;), so data availability is Ethereum-grade (see &lt;a href="https://swiftnodes.io/blog/rollup-vs-validium-vs-optimium" rel="noopener noreferrer"&gt;rollup vs validium&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Connecting is completely standard EVM:&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;scroll&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;534352&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;Scroll&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/scroll?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;scroll&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="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;// just works&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The headline: "equivalent," not just "compatible"
&lt;/h2&gt;

&lt;p&gt;This distinction sounds like marketing until it bites you, so it's worth being precise. A &lt;strong&gt;compatible&lt;/strong&gt; EVM (like zkSync Era) runs your Solidity, but through a different compiler and VM — which means some low-level things differ (custom deploy semantics, non-standard &lt;code&gt;CREATE2&lt;/code&gt; behavior, its own account-abstraction model, occasional edge cases in bytecode-level tricks). An &lt;strong&gt;equivalent&lt;/strong&gt; EVM like Scroll targets &lt;em&gt;identical opcode behavior&lt;/em&gt;, so:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Your existing bytecode runs as-is.&lt;/strong&gt; Contracts compiled for Ethereum mainnet behave the same on Scroll — no recompilation for a different VM, no dialect.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your audits still count.&lt;/strong&gt; Because the execution semantics match, a contract audited for mainnet doesn't acquire new behavior on Scroll.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tooling is unchanged.&lt;/strong&gt; Foundry, Hardhat, ethers, viem, and standard debuggers work without special plugins, because there's nothing non-standard to accommodate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The practical upshot: if you want a zkEVM where "it just works, exactly like mainnet" is the design goal, Scroll is built for that. The ZK part is invisible to your code — it's the proving system underneath, not something you interact with through the RPC.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finality: fast, and &lt;em&gt;actually&lt;/em&gt; final in ~1 hour
&lt;/h2&gt;

&lt;p&gt;Here's where Scroll's ZK nature pays off versus the optimistic rollups we've covered (&lt;a href="https://swiftnodes.io/blog/blast-rpc-native-yield" rel="noopener noreferrer"&gt;Blast&lt;/a&gt;, &lt;a href="https://swiftnodes.io/blog/unichain-rpc-uniswap-l2" rel="noopener noreferrer"&gt;Unichain&lt;/a&gt;, Base):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Soft confirmation (~3s)&lt;/strong&gt; at the sequencer — fast, and what your users feel.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hard finality in ~1 hour&lt;/strong&gt; — once the validity proof is generated and verified on Ethereum, the state is cryptographically final. There's no 7-day fraud-proof window, because there's nothing to dispute: the proof &lt;em&gt;is&lt;/em&gt; the guarantee.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For anything moving value L2→L1 — bridges, withdrawals, cross-rollup flows — that's a big practical difference: hours, not a week. The two-tier model is still worth understanding (&lt;a href="https://swiftnodes.io/blog/l2-finality-soft-vs-hard" rel="noopener noreferrer"&gt;soft vs. hard finality&lt;/a&gt;): the sequencer head is fast but reorg-capable until the proof lands, so key your indexer on block hash and reconcile (&lt;a href="https://swiftnodes.io/blog/handling-chain-reorgs-indexer" rel="noopener noreferrer"&gt;handling chain reorgs&lt;/a&gt;). But the &lt;em&gt;hard&lt;/em&gt; side resolves far quicker than optimistic chains.&lt;/p&gt;

&lt;p&gt;Contrast the other ZK approaches: &lt;a href="https://swiftnodes.io/blog/starknet-rpc-non-evm" rel="noopener noreferrer"&gt;Starknet&lt;/a&gt; also uses STARK validity proofs but is &lt;strong&gt;non-EVM&lt;/strong&gt; (Cairo, &lt;code&gt;starknet_*&lt;/code&gt;), and &lt;a href="https://swiftnodes.io/blog/zksync-era-rpc-differences" rel="noopener noreferrer"&gt;zkSync Era&lt;/a&gt; is a compatible-but-not-equivalent zkEVM. Scroll's pitch is validity proofs &lt;em&gt;with&lt;/em&gt; full EVM equivalence — ZK security, zero relearning.&lt;/p&gt;

&lt;h2&gt;
  
  
  What carries over unchanged (essentially everything)
&lt;/h2&gt;

&lt;p&gt;Because Scroll is bytecode-equivalent, treat it as Ethereum-with-cheaper-gas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&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_estimateGas&lt;/code&gt;, &lt;code&gt;eth_sendRawTransaction&lt;/code&gt;, &lt;code&gt;eth_subscribe&lt;/code&gt; all behave normally.&lt;/li&gt;
&lt;li&gt;Solidity contracts, ABIs, bytecode, and the full viem/ethers/hardhat/foundry toolchain deploy and run &lt;strong&gt;without modification&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;ETH is the 18-decimal gas token; fees include an L1 data-fee component like any rollup (&lt;a href="https://swiftnodes.io/blog/estimating-gas-eth-estimategas-eip-1559" rel="noopener noreferrer"&gt;gas estimation basics&lt;/a&gt;); WebSocket subscriptions work; at ~3s blocks, stream rather than tight-poll.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Scroll (chain ID 534352) is a &lt;strong&gt;bytecode-equivalent zkEVM&lt;/strong&gt; — it targets opcode-level equality with Ethereum, so existing contracts, audits, and tooling work with &lt;strong&gt;zero changes&lt;/strong&gt;, and the RPC is plain &lt;code&gt;eth_*&lt;/code&gt; (viem/ethers/foundry unchanged). As a &lt;strong&gt;ZK rollup&lt;/strong&gt; it posts data to Ethereum (blobs, so Ethereum-grade DA) and reaches &lt;strong&gt;hard finality in ~1 hour&lt;/strong&gt; via validity proofs — no 7-day optimistic window. The key distinction to know: Scroll aims for EVM &lt;em&gt;equivalence&lt;/em&gt;, versus zkSync Era's EVM &lt;em&gt;compatibility&lt;/em&gt; (different VM) and Starknet's non-EVM Cairo. If you want ZK security without relearning anything, this is the zkEVM that behaves like mainnet — just cheaper and provably correct.&lt;/p&gt;

&lt;p&gt;Building on Scroll — DeFi that needs fast L1 finality, cross-rollup apps, or on-chain games? A flat-rate &lt;a href="https://swiftnodes.io/scroll-rpc" rel="noopener noreferrer"&gt;Scroll RPC endpoint&lt;/a&gt; gives you chain 534352 over HTTP and WebSocket alongside 75+ other chains under one key. &lt;a href="https://swiftnodes.io/" rel="noopener noreferrer"&gt;Grab a free key&lt;/a&gt; and point your stack 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/scroll?key=YOUR_API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://swiftnodes.io/blog/scroll-rpc-bytecode-equivalent-zkevm" 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>Rollup vs Validium vs Optimium: Where Your L2's Data Actually Lives</title>
      <dc:creator>SwiftNodes</dc:creator>
      <pubDate>Tue, 11 Aug 2026 05:37:05 +0000</pubDate>
      <link>https://dev.to/swiftnodes/rollup-vs-validium-vs-optimium-where-your-l2s-data-actually-lives-2d23</link>
      <guid>https://dev.to/swiftnodes/rollup-vs-validium-vs-optimium-where-your-l2s-data-actually-lives-2d23</guid>
      <description>&lt;p&gt;You keep seeing the words — rollup, validium, optimium, "modular DA," "settles to Ethereum." They sound like marketing, but they describe a real, consequential choice every L2 makes about &lt;strong&gt;where your transaction data lives&lt;/strong&gt; and &lt;strong&gt;how the chain proves it did the right thing.&lt;/strong&gt; Those two decisions determine how cheap the chain is, how strong its security is, and — the part that actually matters when things go wrong — whether you could recover your funds if the operator vanished. Here's the taxonomy without the jargon, and why a developer should care.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two independent questions
&lt;/h2&gt;

&lt;p&gt;Every L2 answers two separate questions. People conflate them, but they're orthogonal:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;How does it prove its state is correct?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Validity proofs (ZK):&lt;/strong&gt; every state update ships with a cryptographic proof that it's correct. Ethereum verifies the proof; if it's valid, the state is final. No waiting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fraud proofs (optimistic):&lt;/strong&gt; state updates are assumed correct, and there's a challenge window during which anyone can submit a proof that they're &lt;em&gt;wrong&lt;/em&gt;. If no one challenges, it's accepted.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Where does the data availability (DA) live?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;On Ethereum:&lt;/strong&gt; the transaction data is posted to Ethereum (as calldata or, since Dencun, &lt;a href="https://swiftnodes.io/blog/what-are-blobs-eip-4844" rel="noopener noreferrer"&gt;blobs&lt;/a&gt;). Anyone can reconstruct the full L2 state from Ethereum alone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Off Ethereum:&lt;/strong&gt; the data is posted somewhere else — a separate DA layer like &lt;a href="https://swiftnodes.io/blog/celestia-rpc-modular-da" rel="noopener noreferrer"&gt;Celestia&lt;/a&gt;, EigenDA, or a committee — and Ethereum only sees a commitment to it.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The DA question is the one that names the category. &lt;strong&gt;DA is the thing that lets anyone rebuild the chain and exit their funds without the operator's cooperation.&lt;/strong&gt; That's why "where the data lives" is the load-bearing decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  The four combinations
&lt;/h2&gt;

&lt;p&gt;Cross those two axes and you get the taxonomy:&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;&lt;strong&gt;DA on Ethereum&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;DA off Ethereum&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Validity (ZK) proofs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;ZK Rollup&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Validium&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fraud (optimistic) proofs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Optimistic Rollup&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Optimium&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ZK Rollup&lt;/strong&gt; — proofs &lt;em&gt;and&lt;/em&gt; data on Ethereum. The strongest security profile: correctness is proven, and data is recoverable from L1. (e.g. zkSync Era, Scroll, Linea, Starknet.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optimistic Rollup&lt;/strong&gt; — data on Ethereum, correctness enforced by a fraud-proof challenge window. Cheaper historically, at the cost of a ~7-day withdrawal delay. (e.g. Arbitrum, Base, Optimism, Blast.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validium&lt;/strong&gt; — validity proofs, but data kept &lt;strong&gt;off&lt;/strong&gt; Ethereum. Very cheap, and still cryptographically proven correct — but data availability now depends on the external layer, not Ethereum. (e.g. &lt;a href="https://swiftnodes.io/blog/manta-pacific-rpc-modular-da" rel="noopener noreferrer"&gt;Manta Pacific&lt;/a&gt; posts DA to Celestia.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optimium&lt;/strong&gt; — fraud proofs &lt;em&gt;and&lt;/em&gt; off-chain data. The cheapest, and the weakest guarantees: you're trusting both an honest challenger &lt;em&gt;and&lt;/em&gt; an external DA layer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The word "rollup" specifically means &lt;strong&gt;data is on Ethereum.&lt;/strong&gt; The moment a chain moves DA off Ethereum, it's technically a validium or optimium — not a rollup — no matter how it markets itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the DA choice is the one that bites
&lt;/h2&gt;

&lt;p&gt;Here's the scenario that makes it concrete. Suppose the L2's sequencer/operator goes offline permanently or turns malicious. Can you still get your money out?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;On a rollup (data on Ethereum):&lt;/strong&gt; yes. Because the full data is on L1, anyone can reconstruct the latest L2 state and you can force a withdrawal directly through the L1 contracts. Your funds don't depend on the operator being alive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;On a validium/optimium (data off Ethereum):&lt;/strong&gt; only if the external DA layer still has the data and will serve it. If that data becomes unavailable, the L1 contract knows a &lt;em&gt;commitment&lt;/em&gt; to your balance but not the balance itself — and you may be unable to prove what you're owed. That's the &lt;strong&gt;data withholding&lt;/strong&gt; risk, and it's the fundamental trade-off you accept for lower fees.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This isn't hypothetical hand-wringing — it's the exact reason the community draws a hard line between "rollup" (Ethereum-grade DA) and "validium" (borrowed DA). Modern DA layers like Celestia are designed specifically to make off-chain DA trustworthy, so a validium is a reasonable, deliberate engineering choice — but it &lt;em&gt;is&lt;/em&gt; a different security assumption than a rollup, and you should know which one you're building on.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it means for you as a developer
&lt;/h2&gt;

&lt;p&gt;From the RPC, all four types look nearly identical — standard &lt;code&gt;eth_*&lt;/code&gt;, viem/ethers work — so the differences are in how you &lt;em&gt;reason&lt;/em&gt;, not what you call:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Finality timing.&lt;/strong&gt; ZK types can reach hard finality fast (proof verified); optimistic types make you wait out the challenge window for L1 settlement. If you move value L2→L1, this delay is real — see &lt;a href="https://swiftnodes.io/blog/l2-finality-soft-vs-hard" rel="noopener noreferrer"&gt;soft vs. hard finality&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reorg behavior at the head.&lt;/strong&gt; All of them still have a &lt;a href="https://swiftnodes.io/blog/what-is-a-sequencer" rel="noopener noreferrer"&gt;sequencer&lt;/a&gt; ordering transactions, and the L2 head can reorg before settlement — so key your indexer on block hash regardless (&lt;a href="https://swiftnodes.io/blog/handling-chain-reorgs-indexer" rel="noopener noreferrer"&gt;handling chain reorgs&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Long-range trust.&lt;/strong&gt; For anything custodial or high-value, know your chain's DA model. A rollup gives you Ethereum-grade data recoverability; a validium/optimium ties that to an external layer. It doesn't change your code, but it changes your risk model — the same way &lt;a href="https://swiftnodes.io/blog/public-rpc-vs-paid-rpc" rel="noopener noreferrer"&gt;public vs. paid RPC&lt;/a&gt; changes your reliability model without changing your calls.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;L2s vary on two independent axes: &lt;strong&gt;proof system&lt;/strong&gt; (validity/ZK proofs = proven correct + fast finality; fraud/optimistic proofs = assumed correct + challenge window) and &lt;strong&gt;data availability&lt;/strong&gt; (on Ethereum = recoverable from L1; off Ethereum = depends on an external DA layer). Cross them and you get &lt;strong&gt;ZK rollup&lt;/strong&gt;, &lt;strong&gt;optimistic rollup&lt;/strong&gt;, &lt;strong&gt;validium&lt;/strong&gt; (ZK + off-chain DA), and &lt;strong&gt;optimium&lt;/strong&gt; (optimistic + off-chain DA). "Rollup" specifically means data-on-Ethereum; move DA off-chain and it's a validium/optimium, with lower fees bought at the cost of &lt;strong&gt;data-withholding risk&lt;/strong&gt; — whether you could still exit your funds if the operator disappears. It's transparent from the RPC, but it's the difference between Ethereum-grade recoverability and borrowed data availability. Know which one you're on.&lt;/p&gt;

&lt;p&gt;Building across L2s of every flavor? A flat-rate &lt;a href="https://swiftnodes.io/ethereum-rpc" rel="noopener noreferrer"&gt;Ethereum RPC endpoint&lt;/a&gt; plus rollups, validiums, and 75+ other chains under one key — HTTP and WebSocket, no per-call metering. &lt;a href="https://swiftnodes.io/" rel="noopener noreferrer"&gt;Grab a free key&lt;/a&gt; and point your stack 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/eth?key=YOUR_API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://swiftnodes.io/blog/rollup-vs-validium-vs-optimium" 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>web3</category>
      <category>blockchain</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Is Your RPC Node Actually at the Chain Tip? How to Catch a Stale Endpoint</title>
      <dc:creator>SwiftNodes</dc:creator>
      <pubDate>Mon, 10 Aug 2026 06:20:43 +0000</pubDate>
      <link>https://dev.to/swiftnodes/is-your-rpc-node-actually-at-the-chain-tip-how-to-catch-a-stale-endpoint-2k3</link>
      <guid>https://dev.to/swiftnodes/is-your-rpc-node-actually-at-the-chain-tip-how-to-catch-a-stale-endpoint-2k3</guid>
      <description>&lt;p&gt;Here's a failure mode that's quietly common and genuinely nasty: an RPC endpoint that &lt;em&gt;responds&lt;/em&gt; perfectly — 200 OK, valid JSON, every call works — but is serving data from hours or days ago because the node behind it fell behind the chain and never caught up. Your app reads "confirmed" balances that are stale, submits transactions against an old nonce, and shows users numbers that don't match the explorer. Nothing errors. This bites both people running their own node (&lt;a href="https://swiftnodes.io/docs/run-a-node/ethereum" rel="noopener noreferrer"&gt;"is it synced yet?"&lt;/a&gt;) and people &lt;em&gt;consuming&lt;/em&gt; a provider (&lt;a href="https://swiftnodes.io/blog/public-rpc-vs-paid-rpc" rel="noopener noreferrer"&gt;"is this endpoint at the tip, or lagging?"&lt;/a&gt;). The tools to check are simple — but the most obvious one has a trap. Here's how to actually know.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trap: &lt;code&gt;eth_syncing&lt;/code&gt; returns &lt;code&gt;false&lt;/code&gt; for two opposite states
&lt;/h2&gt;

&lt;p&gt;The instinct is to call &lt;code&gt;eth_syncing&lt;/code&gt;. When a node is actively catching up, it returns a rich object:&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;"startingBlock"&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;"currentBlock"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0x1878b4e"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"highestBlock"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0x1878cea"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a node is &lt;strong&gt;fully synced&lt;/strong&gt;, it returns:&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;"jsonrpc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&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;span class="kc"&gt;false&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So far so good — &lt;code&gt;false&lt;/code&gt; means "done syncing," right? &lt;strong&gt;Not exactly.&lt;/strong&gt; &lt;code&gt;eth_syncing&lt;/code&gt; returns &lt;code&gt;false&lt;/code&gt; in &lt;em&gt;two&lt;/em&gt; opposite situations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The node is fully synced and at the tip. ✅&lt;/li&gt;
&lt;li&gt;The node &lt;strong&gt;isn't syncing at all&lt;/strong&gt; — freshly started, stalled, or waiting on its consensus client — so it has &lt;em&gt;nothing to report&lt;/em&gt;. ❌&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A node that's stuck at a block from last week, with its execution head frozen, can happily return &lt;code&gt;eth_syncing: false&lt;/code&gt;. If you treat &lt;code&gt;false&lt;/code&gt; as "healthy," you'll trust a stale node. &lt;strong&gt;&lt;code&gt;eth_syncing&lt;/code&gt; tells you whether a sync is in progress — not whether you're at the tip.&lt;/strong&gt; You need a second, independent check.&lt;/p&gt;

&lt;h2&gt;
  
  
  The real check: compare block height to a reference
&lt;/h2&gt;

&lt;p&gt;The reliable signal is: &lt;strong&gt;does this node's latest block match where the chain actually is right now?&lt;/strong&gt; You can't know "where the chain is" from the node you're suspicious of, so compare against something independent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option A — compare to a reference endpoint.&lt;/strong&gt; Ask your node for its head and ask a known-good endpoint (a public node, a block explorer API, another provider) for theirs, and diff them:&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;mine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ref&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;myClient&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="nx"&gt;refClient&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="p"&gt;]);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lag&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;ref&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;mine&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;lag&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&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;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`node is &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lag&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; blocks behind the reference`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A healthy node is within a block or two of the reference (accounting for propagation). Tens or thousands of blocks behind means it's lagging or stuck.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option B — check the latest block's timestamp against wall-clock.&lt;/strong&gt; This needs &lt;em&gt;no&lt;/em&gt; second endpoint, which makes it great for a self-contained health check. Every block carries a &lt;code&gt;timestamp&lt;/code&gt;; if the newest block is much older than "now," the node isn't keeping up:&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;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;getBlock&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;               &lt;span class="c1"&gt;// latest&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ageSeconds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&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;block&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;ageSeconds&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;60&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;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`newest block is &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ageSeconds&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;s old — node is behind`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pick a threshold a few multiples of the chain's block time (≈60s on Ethereum's ~12s blocks; tighter on fast chains). This is the single most useful "is it at the tip?" check because it catches the frozen-but-responsive node that &lt;code&gt;eth_syncing: false&lt;/code&gt; hides. It's exactly how you'd catch a provider quietly serving stale data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why "it responds" is not "it's synced"
&lt;/h2&gt;

&lt;p&gt;This is the core lesson, and it's worth stating plainly: &lt;strong&gt;a liveness check is not a freshness check.&lt;/strong&gt; Health checks that only confirm the endpoint &lt;em&gt;answers&lt;/em&gt; (&lt;code&gt;eth_blockNumber&lt;/code&gt; returned &lt;em&gt;something&lt;/em&gt;, HTTP 200) will keep a stalled node in rotation, because a stalled node still answers — it just answers with an old number. If you run any kind of failover or load balancing across endpoints, your health check has to assert the head is &lt;em&gt;advancing&lt;/em&gt; and &lt;em&gt;recent&lt;/em&gt;, not merely that a response came back. The cheap version: poll &lt;code&gt;eth_blockNumber&lt;/code&gt; twice a few seconds apart and confirm it moved.&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;a&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;await&lt;/span&gt; &lt;span class="nf"&gt;sleep&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;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;b&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;b&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;a&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;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;head not advancing — node may be stuck&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;h2&gt;
  
  
  Sanity-check finality too, not just height
&lt;/h2&gt;

&lt;p&gt;Being at the tip isn't the whole story on chains with a separate finality signal. A node can be at the head block yet that block isn't final (and could be reorged), or — on some setups — the head advances while &lt;em&gt;finalization&lt;/em&gt; stalls. Where it matters (exchanges, settlement), also read the &lt;code&gt;finalized&lt;/code&gt; block tag and confirm it's recent and progressing:&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;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;If the head is moving but &lt;code&gt;finalized&lt;/code&gt; is stuck far behind, something is wrong with the consensus side even though &lt;code&gt;eth_syncing&lt;/code&gt; and block height look fine. See &lt;a href="https://swiftnodes.io/blog/l2-finality-soft-vs-hard" rel="noopener noreferrer"&gt;soft vs. hard finality&lt;/a&gt; for why the two can diverge, and &lt;a href="https://swiftnodes.io/blog/handling-chain-reorgs-indexer" rel="noopener noreferrer"&gt;handling chain reorgs&lt;/a&gt; for keying data safely on the head.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solana equivalents
&lt;/h2&gt;

&lt;p&gt;Non-EVM chains have their own versions of the same idea. On &lt;a href="https://swiftnodes.io/docs/run-a-node/solana" rel="noopener noreferrer"&gt;Solana&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;getHealth&lt;/code&gt;&lt;/strong&gt; — returns &lt;code&gt;"ok"&lt;/code&gt; when the node is within a small slot distance of the cluster tip; returns an error (with how many slots behind) when it's lagging. This is a real freshness check, unlike &lt;code&gt;eth_syncing&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;getSlot&lt;/code&gt;&lt;/strong&gt; vs a reference — compare your node's current slot to a known-good endpoint's, same as the block-height diff above.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;solana catchup --our-localhost&lt;/code&gt;&lt;/strong&gt; — from the CLI, tells you exactly how far behind the cluster your node is and whether it's closing the gap.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The principle carries across every chain: &lt;strong&gt;confirm the head is recent and advancing against an independent reference — don't trust a single "healthy" boolean.&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;code&gt;eth_syncing&lt;/code&gt; returning &lt;code&gt;false&lt;/code&gt; does &lt;strong&gt;not&lt;/strong&gt; prove a node is at the tip — it returns &lt;code&gt;false&lt;/code&gt; both when fully synced &lt;em&gt;and&lt;/em&gt; when not syncing at all (stalled/stuck). To really know: &lt;strong&gt;compare the node's latest block to an independent reference&lt;/strong&gt; (another endpoint or explorer), and/or &lt;strong&gt;check the newest block's timestamp against wall-clock&lt;/strong&gt; (a block much older than "now" = the node is behind). Remember that a &lt;strong&gt;liveness check is not a freshness check&lt;/strong&gt; — a stalled node still answers &lt;code&gt;eth_blockNumber&lt;/code&gt;, so any health check driving failover must assert the head is &lt;em&gt;advancing and recent&lt;/em&gt;. Sanity-check the &lt;code&gt;finalized&lt;/code&gt; tag where finality matters, and use &lt;code&gt;getHealth&lt;/code&gt; / &lt;code&gt;catchup&lt;/code&gt; as the Solana equivalents. This is how you catch both a self-hosted node that quietly fell behind and a provider serving stale data.&lt;/p&gt;

&lt;p&gt;Want endpoints that are actually at the tip — health-checked and load-balanced so a lagging node gets pulled from rotation? A flat-rate &lt;a href="https://swiftnodes.io/ethereum-rpc" rel="noopener noreferrer"&gt;Ethereum RPC endpoint&lt;/a&gt;, &lt;a href="https://swiftnodes.io/solana-rpc" rel="noopener noreferrer"&gt;Solana RPC&lt;/a&gt;, and 75+ other chains under one key, over HTTP and WebSocket. &lt;a href="https://swiftnodes.io/" rel="noopener noreferrer"&gt;Grab a free key&lt;/a&gt; and point your stack 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/eth?key=YOUR_API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://swiftnodes.io/blog/is-your-rpc-node-synced-stale-endpoint" 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>Berachain RPC: Standard EVM, but the Token You Earn Can't Be Transferred</title>
      <dc:creator>SwiftNodes</dc:creator>
      <pubDate>Sun, 09 Aug 2026 06:43:45 +0000</pubDate>
      <link>https://dev.to/swiftnodes/berachain-rpc-standard-evm-but-the-token-you-earn-cant-be-transferred-2jb</link>
      <guid>https://dev.to/swiftnodes/berachain-rpc-standard-evm-but-the-token-you-earn-cant-be-transferred-2jb</guid>
      <description>&lt;p&gt;Berachain is one of the rare chains where the &lt;em&gt;RPC&lt;/em&gt; is completely ordinary and the &lt;em&gt;economics&lt;/em&gt; are the thing you have to understand. It's an &lt;strong&gt;EVM-identical Layer 1&lt;/strong&gt; (chain ID &lt;strong&gt;80094&lt;/strong&gt;) — your Solidity, ABIs, viem/ethers/foundry, and &lt;code&gt;eth_*&lt;/code&gt; calls all work with zero changes. What's different is underneath: a &lt;strong&gt;Proof-of-Liquidity&lt;/strong&gt; consensus and a &lt;strong&gt;three-token model&lt;/strong&gt; where one of the tokens, &lt;strong&gt;BGT&lt;/strong&gt;, is something you &lt;em&gt;earn but cannot transfer&lt;/em&gt;. Treat BGT like a normal ERC-20 and your integration will fail in a way the RPC won't warn you about. Here's the map — mostly "everything works," with the two things that genuinely differ.&lt;/p&gt;

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

&lt;p&gt;Berachain mainnet (live since February 2025) is &lt;strong&gt;chain ID 80094&lt;/strong&gt;, an EVM-identical L1 with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Three tokens, each with a job:&lt;/strong&gt; &lt;strong&gt;BERA&lt;/strong&gt; (native gas token, 18 decimals), &lt;strong&gt;HONEY&lt;/strong&gt; (a native stablecoin), and &lt;strong&gt;BGT&lt;/strong&gt; (governance/emissions token — &lt;strong&gt;non-transferable&lt;/strong&gt;, earned by providing liquidity).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proof-of-Liquidity (PoL) consensus&lt;/strong&gt; — validator rewards are tied to liquidity provided to the ecosystem, not raw staking.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BeaconKit on CometBFT&lt;/strong&gt; — ~2-second blocks with &lt;strong&gt;single-slot deterministic finality&lt;/strong&gt; (an "EVM cockpit over a CometBFT engine," like &lt;a href="https://swiftnodes.io/blog/kaia-rpc-klaytn-finschia" rel="noopener noreferrer"&gt;Kaia&lt;/a&gt;, &lt;a href="https://swiftnodes.io/blog/sei-rpc-parallel-evm" rel="noopener noreferrer"&gt;Sei&lt;/a&gt;, and &lt;a href="https://swiftnodes.io/blog/story-rpc-programmable-ip" rel="noopener noreferrer"&gt;Story&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EVM-identical&lt;/strong&gt; — Solidity, ABIs, and the full Ethereum toolchain deploy unchanged.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Connecting is completely standard EVM:&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;berachain&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;80094&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;Berachain&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;Bera&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;BERA&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/berachain?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;berachain&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="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;// just works&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The gotcha: BGT is earned, not transferred
&lt;/h2&gt;

&lt;p&gt;This is the one that catches integrations. In a normal ERC-20 world, if you hold a token you can send it — &lt;code&gt;transfer&lt;/code&gt;, &lt;code&gt;transferFrom&lt;/code&gt;, approve a spender, done. &lt;strong&gt;BGT breaks that assumption on purpose.&lt;/strong&gt; BGT is a &lt;strong&gt;non-transferable&lt;/strong&gt; (soulbound-style) governance token: you &lt;em&gt;earn&lt;/em&gt; it by providing liquidity under Proof-of-Liquidity, and you can delegate it to validators, use it to vote, or &lt;strong&gt;burn/redeem it for BERA&lt;/strong&gt; — but you &lt;strong&gt;cannot send it to another address&lt;/strong&gt; like a normal token.&lt;/p&gt;

&lt;p&gt;What that means when you build:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Don't expose a "send BGT" flow.&lt;/strong&gt; A wallet or app that renders BGT as a transferable balance and offers a transfer button will produce transactions that revert. The RPC will happily accept the call and the transaction will fail on-chain — a confusing bug if you assumed ERC-20 semantics.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model it as position + redemption, not spendable balance.&lt;/strong&gt; You read a BGT balance with &lt;code&gt;balanceOf&lt;/code&gt; like any token (&lt;a href="https://swiftnodes.io/blog/reading-balances-eth-getbalance-balanceof" rel="noopener noreferrer"&gt;reading balances right&lt;/a&gt;), but the &lt;em&gt;actions&lt;/em&gt; available are delegate / vote / redeem-for-BERA, not transfer. Design your UI and accounting around that.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BERA and HONEY are normal.&lt;/strong&gt; BERA (gas) and HONEY (stablecoin) behave like ordinary tokens/native coin — it's specifically &lt;strong&gt;BGT&lt;/strong&gt; that has the transfer restriction. Don't over-apply the rule.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you take one thing from this post: &lt;strong&gt;check which of the three tokens you're touching, and remember BGT is the non-transferable one.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Proof-of-Liquidity, briefly (and why it barely touches the RPC)
&lt;/h2&gt;

&lt;p&gt;Proof-of-Liquidity is the reason BGT exists. Instead of paying block rewards purely to stakers, Berachain routes emissions (as BGT) to &lt;strong&gt;liquidity providers and the protocols they support&lt;/strong&gt;, aiming to keep liquidity — and therefore security — on-chain. It drove a large pre-launch ecosystem of DeFi protocols designed around BGT incentives.&lt;/p&gt;

&lt;p&gt;For a developer, PoL is mostly an &lt;strong&gt;economic/protocol&lt;/strong&gt; layer, not an RPC one. You interact with the reward and liquidity contracts the same way you'd call any contract (&lt;code&gt;eth_call&lt;/code&gt; to read, &lt;code&gt;eth_sendRawTransaction&lt;/code&gt; to act); there's no special namespace. The one place it &lt;em&gt;does&lt;/em&gt; surface is the BGT rule above — that's PoL's design showing through into token behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finality: single-slot, so confirm once
&lt;/h2&gt;

&lt;p&gt;BeaconKit on CometBFT gives &lt;strong&gt;single-slot deterministic finality&lt;/strong&gt; — a committed block is final, no reorgs. Practically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Skip the reorg machinery.&lt;/strong&gt; The &lt;a href="https://swiftnodes.io/blog/handling-chain-reorgs-indexer" rel="noopener noreferrer"&gt;reorg-handling patterns&lt;/a&gt; collapse to "confirm once" — no deep confirmation counts, no block-hash reconciliation for probabilistic reorgs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;~2-second blocks&lt;/strong&gt; — fast enough that WebSocket subscriptions (&lt;code&gt;newHeads&lt;/code&gt;, &lt;code&gt;logs&lt;/code&gt;) beat tight polling for live data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's the same fast-finality comfort as the other CometBFT-EVM and BFT-EVM chains we've covered — &lt;a href="https://swiftnodes.io/blog/kaia-rpc-klaytn-finschia" rel="noopener noreferrer"&gt;Kaia&lt;/a&gt;, &lt;a href="https://swiftnodes.io/blog/sei-rpc-parallel-evm" rel="noopener noreferrer"&gt;Sei&lt;/a&gt;, &lt;a href="https://swiftnodes.io/blog/monad-rpc-parallel-evm" rel="noopener noreferrer"&gt;Monad&lt;/a&gt; — where "final means final" once you see the block.&lt;/p&gt;

&lt;h2&gt;
  
  
  What carries over unchanged (almost everything)
&lt;/h2&gt;

&lt;p&gt;Because Berachain is EVM-identical, treat it as a standard EVM chain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&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_estimateGas&lt;/code&gt;, &lt;code&gt;eth_sendRawTransaction&lt;/code&gt;, &lt;code&gt;eth_subscribe&lt;/code&gt; all behave normally.&lt;/li&gt;
&lt;li&gt;Solidity contracts, ABIs, and the viem/ethers/hardhat/foundry toolchain deploy and run as-is.&lt;/li&gt;
&lt;li&gt;BERA is the 18-decimal gas token; gas follows standard EVM mechanics (&lt;a href="https://swiftnodes.io/blog/estimating-gas-eth-estimategas-eip-1559" rel="noopener noreferrer"&gt;gas estimation basics&lt;/a&gt;); WebSocket subscriptions work.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Berachain (chain ID 80094) is an &lt;strong&gt;EVM-identical&lt;/strong&gt; L1 — standard &lt;code&gt;eth_*&lt;/code&gt;, viem/ethers/foundry unchanged — built on &lt;strong&gt;Proof-of-Liquidity&lt;/strong&gt; consensus with a &lt;strong&gt;tri-token model&lt;/strong&gt;: &lt;strong&gt;BERA&lt;/strong&gt; (gas), &lt;strong&gt;HONEY&lt;/strong&gt; (native stablecoin), and &lt;strong&gt;BGT&lt;/strong&gt; (governance, earned via liquidity). The one thing that will bite you is that &lt;strong&gt;BGT is non-transferable&lt;/strong&gt; — you earn, delegate, vote, or redeem it for BERA, but you cannot &lt;code&gt;transfer&lt;/code&gt; it, so any flow that treats it like a normal ERC-20 will revert. PoL is otherwise an economic layer that barely touches the RPC, and &lt;strong&gt;single-slot CometBFT finality&lt;/strong&gt; means you confirm once (no reorgs). Build it like any EVM chain — just get the BGT rule right.&lt;/p&gt;

&lt;p&gt;Building liquidity-incentivized DeFi, HONEY stablecoin apps, or anything indexing Berachain? A flat-rate &lt;a href="https://swiftnodes.io/berachain-rpc" rel="noopener noreferrer"&gt;Berachain RPC endpoint&lt;/a&gt; gives you chain 80094 over HTTP and WebSocket alongside 75+ other chains under one key. &lt;a href="https://swiftnodes.io/" rel="noopener noreferrer"&gt;Grab a free key&lt;/a&gt; and point your stack 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/berachain?key=YOUR_API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://swiftnodes.io/blog/berachain-rpc-proof-of-liquidity" 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>
