guide10 min read

Sonic Mainnet RPC for Wallet Builders

What to check before a wallet points at Sonic mainnet: chain ID 146 / 0x92, S gas, sub-second finality, Fantom Opera legacy confusion, public endpoints that passed an 8-round probe, and public vs dedicated RPC.

By RPC Directory•
#Sonic#S#RPC#Wallet#Chain ID#Fantom#Opera#EVM
$sonic-mainnet-rpc.md

Sonic is an EVM Layer 1 with the native S token for fees, staking, and governance. Docs describe about 400,000 TPS and sub-second finality, with no rollbacks or reorganizations. Gas is paid in S, not ETH and not FTM. Wallet builders who paste Fantom Opera chain ID 250, reuse an Opera RPC, or show S balances without the network name will ship the wrong chain or confuse users who still hold FTM.

This is not a ranking of providers. Use the Sonic Mainnet hub to compare endpoints and paid rows, then run the checks below on the URL you configure. The Sonic Mainnet providers page lists the keyed options side by side.

Chain ID 146 is not Fantom Opera and not Ethereum

  • Sonic mainnet: chain ID 146, hex 0x92. This is what wallets, eth_chainId, and EIP-155 signatures use. eth_getRules on the official RPC returns NetworkID 146.
  • Fantom Opera (legacy): chain ID 250, hex 0xfa. Sonic Labs documents Sonic as a migration from the Fantom chain and FTM token. Opera continues to run, but development focus moved to Sonic after the 18 December 2024 launch. FTM holders upgrade to S through the published upgrade flow.
  • Ethereum mainnet: chain ID 1. A wallet that already has Ethereum or Opera added still needs a separate Sonic network entry.

Wallet-add fields for mainnet, matching Sonic's published network information:

  • Network name: Sonic
  • Chain ID: 146 (or 0x92 if the form wants hex)
  • Currency symbol: S
  • RPC URL: a JSON-RPC HTTPS endpoint you have already probed, such as the official https://rpc.soniclabs.com
  • Block explorer: https://sonicscan.org

Native gas is S with 18 decimals. The symbol is unique, so the main mix-up is Opera vs Sonic, not L1 ETH vs L2 ETH. Show the network name next to every balance and fee. Call eth_chainId on the exact RPC URL you will ship. Expect 0x92. A gateway that returns 0xfa is Opera. A gateway that returns 0x1 is Ethereum. A timeout, an HTML page, a keyed-only error, or a rate-limit body is not Sonic mainnet for that transport.

Public endpoints that answered eth_chainId

We probed every public Sonic mainnet URL on the hub 8 times, about 55 seconds apart, on 2026-10-07 between 02:11 and 02:19 UTC. Each round called eth_chainId, eth_getBlockByNumber("latest"), and eth_getBalance on the zero address, with a curl User-Agent on every request. The URLs below passed all three calls in 8 of 8 rounds (OnFinality noted separately):

EndpointTransportNotes
https://rpc.soniclabs.comHTTPSOfficial. Head was 12 to 19 seconds old during the test, 4 to 26 blocks behind the freshest public URL
https://sonic.drpc.org / wss://sonic.drpc.orgHTTPS, WSSHead 12 to 15 seconds old
https://sonic-rpc.publicnode.com / wss://sonic-rpc.publicnode.comHTTPS, WSSHead 9 to 15 seconds old
https://rpc.sentio.xyz/sonic-mainnet / https://sonic-mainnet.rpc.sentio.xyzHTTPSHead 7 to 11 seconds old
https://rpc.swiftnodes.io/rpc/sonicHTTPSHead 5 to 10 seconds old
https://sonic-json-rpc.stakely.ioHTTPSHead 5 to 13 seconds old
https://sonic-mainnet.gateway.tatum.ioHTTPSHead 4 to 7 seconds old
https://sonic.api.pocket.networkHTTPSHead 2 to 4 seconds old, freshest in every round
https://rpc.nodeflare.app/sonic/publicHTTPS8 of 8 with a curl User-Agent, head 8 to 11 seconds old. Returns HTTP 403 to some default User-Agents (Node's built-in fetch, Python urllib, Go net/http) and to requests with none, so set a User-Agent on server-side clients. Browsers, curl, ethers, axios, node-fetch, requests, and httpx worked

https://sonic.api.onfinality.io/public passed 6 of 8 rounds. Two rounds returned HTTP 504 from the gateway. It stays listed with that caveat.

Four other rows failed all 8 rounds with a curl User-Agent, and the directory now hides them: Ankr's public Sonic URL requires an API key, lb.routeme.sh/rpc/evm/146 returned a public rate-limit error on every try, sonic.therpc.io never answered, and wss://sonic.callstaticrpc.com failed the TLS handshake. A row on a directory page is a starting point, not proof it serves Sonic mainnet today.

Fees on Sonic: 50 gwei MinBaseFee, use the ~55 gwei RPC suggestion

Sonic uses an EIP-1559-style base fee plus priority fee. Unlike Ethereum, Sonic docs state the base fee is not burned. 90% of fees go to the FeeM treasury for app developers and 10% to validators.

During the test on the official RPC:

  • eth_gasPrice returned 0xcce416600 (55 gwei). Sonic docs say this is the 50 gwei minimum base fee plus a 10% buffer.
  • eth_feeHistory base fees were 0xcce416600 across the sampled window, with priority rewards of 1 wei.
  • eth_maxPriorityFeePerGas returned 0x1 (1 wei). Sonic docs say that is the normal minimum tip when the network is not congested.
  • eth_estimateGas for a plain S transfer returned 0x5244 (21,060).

What that means for a wallet:

  • Do not apply Ethereum L1 tip defaults. A priority fee of 1 wei is expected here. An L1-style 1 gwei tip overpays by orders of magnitude when the mempool is quiet.
  • Do not cap fees at the raw 50 gwei MinBaseFee. Sonic's transaction pool needs more than a 5% buffer above the base fee. Use eth_gasPrice or eth_feeHistory (both return about 55 gwei, a 10% buffer) or add at least 6 to 10% yourself. Setting maxFeePerGas to exactly 50 gwei plus a 1 wei tip is rejected as underpriced.
  • Add a buffer on complex estimates. Sonic docs warn that eth_estimateGas can under-estimate state-dependent calls and suggest a 20 to 30% gas-limit buffer. Unused gas is refunded.
  • No L1 data fee. Sonic is an L1, so total fee is gasUsed * effectiveGasPrice. "Send max" does not need an OP Stack L1 fee subtraction.

Finality and block timing that matter for wallets

Across 20 consecutive blocks on PublicNode during the test, timestamps spanned 16 seconds (about 0.84 seconds per block). In the same read, latest, safe, and finalized all returned the same block height. That matches Sonic's claim of sub-second finality with no reorg risk: a latest receipt is already final for practical wallet UI.

Sonic Gateway bridges assets between Ethereum and Sonic. Docs describe deposits that take about 15 minutes on Ethereum to finalize, about 1 second on Sonic, then a heartbeat that batches assets every about 10 minutes. Withdrawals and bridge UX are separate from ordinary Sonic sends. Do not treat Gateway timing as Sonic block finality.

web3_clientVersion on the official RPC returned Sonic/v2.2.4-... during the test, so a client-version health check works here (unlike some OP Stack public RPCs that 403 that method).

MetaMask, Rabby, and Rainbow

Sonic's docs list MetaMask, Rabby, OKX, Trust, Bitget, and 1inch among user wallets, plus Safe and institutional options. All three of MetaMask, Rabby, and Rainbow accept Sonic as an EVM network with the fields above.

  • MetaMask: the add-network form takes the five fields directly. When a dapp calls wallet_switchEthereumChain for a chain MetaMask does not know, it returns error 4902. Handle that by calling wallet_addEthereumChain.
  • Rabby: check whether Sonic already appears in Rabby's network list before adding a custom entry.
  • Rainbow: accepts the same EIP-3085 add request. Confirm the explorer URL is sonicscan.org.

For wallet_addEthereumChain, send a full EIP-3085 payload: chainId: "0x92", chainName: "Sonic", nativeCurrency: { name: "Sonic", symbol: "S", decimals: 18 }, rpcUrls with your probed HTTPS endpoint, and blockExplorerUrls: ["https://sonicscan.org"]. For wallet_switchEthereumChain, send only { chainId: "0x92" }. Prefer one primary RPC in the wallet payload and keep failover in your own client.

Wallet-add gotchas that show up in QA

  1. Chain ID 250 in the Sonic row: the network name says Sonic but signatures target Fantom Opera.
  2. Opera RPC behind a Sonic label: eth_chainId returns 0xfa, balances and nonces are Opera state.
  3. FTM symbol on a Sonic network entry: users think they still hold FTM on Opera.
  4. Ethereum-style priority fees: overpays when Sonic returns a 1 wei tip estimate.
  5. Underpriced at the raw 50 gwei base fee: transactions rejected unless you add the buffer Sonic docs require (use the ~55 gwei RPC suggestion).
  6. One shared "fantom" or "sonic" key for Opera and Sonic: separate config keys so a fallback cannot silently swap networks.
  7. Server-side client with no User-Agent against Nodeflare: HTTP 403 even though browsers and curl succeed.

Public vs dedicated for wallet traffic

Public endpoints are fine for:

  • Local development and wallet QA
  • Low-volume eth_chainId / eth_blockNumber probes
  • One-off scripts and docs examples

They are a poor primary path when you:

  • Run production wallet RPC for many users (balance, nonce, fee estimate, send, receipt polls)
  • Need predictable latency and a support channel when the gateway rate limits you
  • Depend on historical state, wide eth_getLogs, or sustained WebSocket traffic
  • Cannot absorb shared-node brownouts during a launch, airdrop, or market spike

Probe the candidate URL before you call it ready:

  1. eth_chainId returns 0x92.
  2. The latest block advances when you poll twice a few seconds apart, and its timestamp is within a few seconds of wall-clock time.
  3. A plain transfer eth_estimateGas returns a value near 21,000.
  4. A burst of 20 to 50 quick requests does not return HTTP 429 or a JSON-RPC rate-limit error at the volume you expect per client.
  5. If you need push updates, a WebSocket eth_subscribe that survives a disconnect.
  6. If you call from Node, Python, or Go without a library User-Agent, confirm the URL still answers (or set one).

Public rows on the hub are shared and rate-limited. Use them for the probe. For production wallet traffic, compare the paid rows on the same hub for method coverage, archive access, and quota. At the time of writing those include Alchemy, Chainstack, DRPC, and Dwellir. Dwellir's row is a keyed Sonic mainnet archive endpoint over HTTPS and WSS. It is not the only option.

A cheaper URL that fails the probe above is not a savings. The public vs paid guide covers when a shared endpoint stops being enough, and the pricing calculator estimates monthly cost for a given request mix. Log-heavy indexers should also read the eth_getLogs caps guide, and the dead public endpoints guide explains why listed URLs stop answering.

Practical split for a wallet stack

  • User-facing Sonic network row: chain ID 146 / 0x92, S symbol with the network name shown, SonicScan, RPC you probed today.
  • Opera row, if you still need it: chain ID 250 / 0xfa, separate config key, never reused as the Sonic RPC.
  • Fee estimation: use eth_gasPrice or eth_feeHistory (~55 gwei, already buffered above the 50 gwei MinBaseFee), tip often 1 wei, gas-limit buffer on complex calls, refreshed per send.
  • Backend / indexer: dedicated or paid endpoint with explicit method and archive needs. Do not share the browser wallet's public URL.

Ship the hub comparison and the probe first. A network called "Sonic" in a dropdown does not replace eth_chainId returning 0x92.