guide13 min read

Celo Mainnet RPC for Wallet Builders

What to check before a wallet points at Celo mainnet: chain ID 42220 / 0xa4ec, CELO gas and token duality, CIP-64 fee currencies and which wallets honor them, the 200 gwei minimum base fee, OP Stack safe and finalized tags, the 7-day withdrawal, verified public endpoints, and public vs dedicated RPC.

By RPC Directory•
#Celo#CELO#RPC#Wallet#Chain ID#OP Stack
$celo-mainnet-rpc.md

Celo has been an OP Stack Layer 2 on Ethereum since 26 March 2025, when the old Celo L1 stopped at block 31,056,499 and the L2 continued from block 31,056,500 with the same chain ID and state. Gas is paid in CELO, or in an allowlisted ERC-20 such as USDC, USD₮ or USDm through Celo's fee abstraction, but only when the wallet builds the right transaction type.

Wallet builders who paste chain ID 1, reuse an Alfajores or Celo Sepolia config, treat CELO as two separate balances, or assume a feeCurrency field will survive every wallet will ship the wrong network or transactions that fail for users who hold no CELO. Before you wire MetaMask, Rabby, Rainbow, or your own wallet_addEthereumChain flow, prove chain ID 42220, price fees the way Celo charges them, and probe the URL you intend to ship.

This is not a ranking of providers. Use the Celo Mainnet hub to compare endpoints and paid rows, then run the checks below on the URL you configure. If you only need the network in a browser wallet, the add Celo to MetaMask, add Celo to Rabby, and add Celo to Rainbow pages have the fields prefilled.

Chain ID 42220 is not Ethereum and not Celo Sepolia

  • Celo mainnet: chain ID 42220, hex 0xa4ec. This is what wallets, eth_chainId, and EIP-155 signatures use. The L2 migration kept it, so pre-2025 configs still carry the right ID.
  • Celo Sepolia testnet: chain ID 11142220, hex 0xaa044c, RPC https://forno.celo-sepolia.celo-testnet.org. It replaced Alfajores as Celo's developer testnet and settles to Ethereum Sepolia.
  • Ethereum mainnet: chain ID 1. A wallet that already has Ethereum added still needs a separate Celo network entry.

Wallet-add fields for mainnet, matching the parameters Celo publishes for its own add-network button:

  • Network name: Celo Mainnet
  • Chain ID: 42220 (or 0xa4ec if the form wants hex)
  • Currency symbol: CELO
  • RPC URL: a JSON-RPC HTTPS endpoint you have already probed, such as the official https://forno.celo.org
  • Block explorer: https://celoscan.io (Celo also lists https://explorer.celo.org, which now redirects to https://celo.blockscout.com)

Native gas is CELO with 18 decimals, so 1 CELO = 10^18 wei and parseEther("1") means 1 CELO. CELO also has token duality: the same balance is exposed as an ERC-20 at 0x471EcE3750Da237f93B8E339c536989b8978a438 (its symbol() returns CELO). The contract does not keep its own balances. balanceOf passes through the native balance, and transfer moves native CELO through a precompile. During the test, eth_getBalance and balanceOf on that contract returned the identical value for the same address.

A wallet that lists native CELO and the CELO ERC-20 as two assets shows the user double their real balance, and an indexer that only follows native value transfers misses CELO moved by an ERC-20 transfer call.

Call eth_chainId on the exact RPC URL you will ship. Expect 0xa4ec. A gateway that returns 0xaa044c is Celo Sepolia. A gateway that returns 0x1 is Ethereum. A timeout, an HTML page, or a "paid plan required" body is not Celo mainnet for that transport. Run the check after any load balancer or fallback so you are testing the backend production actually hits.

Public endpoints that answered eth_chainId

Each URL below returned 0xa4ec across eight spaced tries (exceptions noted), a head block within about 10 seconds of wall-clock time, and 0x5208 (21,000) for eth_estimateGas on a plain CELO transfer when this guide was written (2026-10-02, 02:13 to 02:19 UTC):

EndpointTransportNotes
https://forno.celo.org / wss://forno.celo.org/wsHTTPS, WSSOfficial, run by cLabs. Best effort, rate limited, non-archive. Head ran 2 to 9 seconds behind other endpoints during the test. Celo documents that Forno closes WebSocket connections after 20 minutes
https://celo.drpc.org / wss://celo.drpc.orgHTTPS, WSSOne in five eth_getBlockByNumber calls returned -32601 "method does not exist/is not available" during the test, then the next call succeeded
https://rpc.ankr.com/celoHTTPSAnswered eth_chainId, block reads, eth_estimateGas, and fee-currency eth_gasPrice without a key during the test
https://celo-json-rpc.stakely.ioHTTPSRe-checked 2026-10-05: 8 of 8 tries returned 0xa4ec, head within 2 seconds, eth_estimateGas 0x5208
https://rpc.swiftnodes.io/rpc/celoHTTPSRe-checked 2026-10-05: 8 of 8 tries returned 0xa4ec, head within 3 seconds, eth_estimateGas 0x5208
https://42220.rpc.thirdweb.comHTTPSRe-checked 2026-10-05: 8 of 8 tries returned 0xa4ec, head within 8 seconds, eth_estimateGas 0x5208
https://celo-mainnet.gateway.tatum.ioHTTPSRe-checked 2026-10-05: 8 of 8 tries returned 0xa4ec, head within 1 second, eth_estimateGas 0x5208. The free tier returned HTTP 429 once on 2026-10-02 when the test exceeded 5 requests per minute

Other rows on the hub are not listed here. Several of them failed the same probe at that time. One returned a usage-limit error on every call, one returned a public rate-limit error on most calls, and one answered eth_chainId correctly but served a "latest" block more than six years old, so its balances and nonces would be wrong. Another answered eth_chainId and then rate limited the follow-up block read.

A row on a directory page is a starting point, not proof that it serves Celo mainnet today. Re-run the probe before you ship any URL, including the ones above. The dead public RPC endpoints guide covers how listed rows fail and a repeatable health check.

Celo's own docs describe Forno as a free, best-effort service with no uptime guarantee and point production apps at professional providers. Treat the table as a probe list for development and QA, not as a production recommendation.

CELO fees: one base fee, no L1 data fee, optional fee currencies

Celo keeps the EIP-1559 model with a few Celo-specific settings:

  • No L1 data fee and no operator fee. Celo uses EigenDA for data availability and sets the OP Stack L1 fee scalars and operator fee to zero, and the GasPriceOracle predeploy returns zero for the L1 fee. gasUsed effectiveGasPrice is the whole fee, so "send max" can subtract gasLimit maxFeePerGas and stop there. That is a difference from most OP Stack chains.
  • A 200 gwei minimum base fee. Since the Jovian hardfork (mainnet, 31 March 2026), the floor uses the OP Stack minimum base fee. On Ethereum, minBaseFee() on Celo's SystemConfig (0x89E31965D844a309231B1f17759Ccaf1b7c09861) returned 200 gwei. Older Celo pages still list a 25 gwei floor; the on-chain value is the one that applies. During the test the base fee sat at exactly 200 gwei on 795 of the last 1,025 blocks, eth_gasPrice returned about 203 gwei, and eth_maxPriorityFeePerGas returned 2.5 gwei. A plain transfer cost about 0.0043 CELO.
  • Gas prices are CELO-denominated. Do not cap them with an ETH-L2 default. A cap below 200 gwei produces transactions that never get included.
  • Fee currencies price gas in the token. Pass a fee currency address to eth_gasPrice and the node returns the price in that token, always with 18 decimals. During the test, eth_gasPrice with the USDm address returned about 20.3 gwei, so the same transfer cost about 0.00043 USDm. Every endpoint in the table above answered this form of eth_gasPrice.

Fee abstraction depends on the wallet, not on your RPC

Celo's fee abstraction lets an EOA pay gas in an allowlisted ERC-20, at the protocol level, with no paymaster or relayer. The field is feeCurrency on the CIP-64 transaction type (0x7b, 123). The Type 0 transaction with a feeCurrency field from the L1 era is deprecated.

  • The allowlist is on-chain. It lives in the FeeCurrencyDirectory and includes USDC, USD₮, USA₮, WETH, and the Mento stablecoins. Mento's dollar, formerly cUSD, now reports the on-chain symbol USDm at the same address, 0x765DE816845861e75A25fCA122bb6898B8B1282a. Match tokens by address, not symbol.
  • 6-decimal tokens use an adapter. For USDC, USD₮ and USA₮, feeCurrency must be the adapter address, not the token address. For USDC on mainnet that is 0x2F25deB3848C207fc8E0c34035B3Ba7fC157602B, while the USDC token itself is 0xcebA9300f2b948710d2653dD7B07f33A8B32118C.
  • MetaMask drops feeCurrency. Celo's docs say MetaMask keeps only the transaction fields it knows, signs a standard transaction, and charges gas in CELO. A user with no CELO then sees a generic insufficient-funds error. Celo lists MiniPay, Valora, and Ledger Live's own send flow as wallets that build CIP-64, and notes that MiniPay and Valora may pick the fee currency themselves.
  • Keep a CELO path. If a flow depends on paying gas in a stablecoin, either target a wallet that builds CIP-64 or make sure the user can still pay in CELO.

OP Stack quirks that matter for wallets

  • Three block tags, three heights. Blocks arrive every second (1,000 blocks took exactly 1,000 seconds during the test). safe trailed latest by about 280 blocks (4.6 minutes) and finalized by about 1,490 blocks (25 minutes). Show a latest receipt as included, and reserve "final" for UI that actually needs Ethereum-backed finality.
  • Canonical withdrawals take at least 7 days and three transactions. The user initiates on Celo, proves on Ethereum once the withdrawal is provable (Celo's guide warns that can take up to 2 hours), and finalizes on Ethereum after the challenge period. On Ethereum, proofMaturityDelaySeconds() on Celo's OptimismPortal (0xc5c5D157928BDBD2ACf6d0777626b6C75a9EAEDC) returned 604,800 (7 days). Celo uses OP Succinct Lite fault proofs since the Jello hardfork in December 2025. Third-party bridges have their own timing.
  • CELO bridges as an ERC-20 on Ethereum, ETH does not bridge natively. On Ethereum, CELO is a standard ERC-20 and deposits go through OptimismPortal.depositERC20Transaction after an approve. The native bridge rejects plain ETH; WETH bridges as an ERC-20 and is on the fee currency allowlist.
  • Pre-migration history needs an archive or historical RPC. Forno is non-archive, and Celo documents a separate historical RPC service for state from before the migration. If your wallet shows old balances or replays old receipts, probe that range on the endpoint you pick.

MetaMask, Rabby, and Rainbow

All three accept Celo as an EVM network with the fields above. What to expect:

  • MetaMask: the add-network form takes the five fields directly. Celo's docs publish a one-click wallet_addEthereumChain button with chain ID 0xa4ec, Celo Mainnet, CELO, Forno, and Celoscan. When a dapp calls wallet_switchEthereumChain for a chain MetaMask does not know, it returns error 4902. Handle that by calling wallet_addEthereumChain. Gas is always charged in CELO through MetaMask, as covered above.
  • Rabby: check whether Celo already appears in Rabby's network list before adding a custom entry, so you do not end up with two Celo rows pointing at different RPCs. Celo's docs do not confirm Rabby's CIP-64 support either way, so test fee-currency flows before you rely on them.
  • Rainbow: accepts the same EIP-3085 add request from a dapp. If your dapp connects wallets with RainbowKit or wagmi, define Celo with the fields above and confirm the explorer URL is celoscan.io. The same fee-currency caveat applies.

For wallet_addEthereumChain, send a full EIP-3085 payload: chainId: "0xa4ec", chainName: "Celo Mainnet", nativeCurrency: { name: "Celo", symbol: "CELO", decimals: 18 }, rpcUrls with your probed HTTPS endpoint, and blockExplorerUrls: ["https://celoscan.io"]. For wallet_switchEthereumChain, send only { chainId: "0xa4ec" }. 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 1 in the Celo row - the network name says Celo but signatures target Ethereum.
  2. Testnet mix-up - 11142220 / 0xaa044c pasted into a mainnet config, or an Alfajores-era config that no longer has a live testnet behind it.
  3. CELO counted twice - the native balance and the CELO ERC-20 at 0x471E...a438 are the same funds.
  4. feeCurrency assumed to work everywhere - MetaMask drops it and charges CELO, and users without CELO see insufficient funds.
  5. Token address used as feeCurrency for USDC or USD₮ - those need the adapter address.
  6. Gas price capped below 200 gwei - the transaction sits below the minimum base fee and never lands.
  7. One shared "celo" RPC key for mainnet and testnet - separate config keys so a fallback cannot silently swap networks.

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 mint, airdrop, or market spike

Probe the candidate URL before you call it ready:

  1. eth_chainId returns 0xa4ec.
  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. A correct chain ID with a stale head is still a failed probe.
  3. eth_estimateGas answers for a plain CELO transfer, and eth_gasPrice answers with and without a fee currency argument if you support fee abstraction.
  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 delivers new heads, and a client that reconnects, resubscribes, and backfills the blocks it missed after a dropped socket. Subscriptions are tied to one connection and do not survive a disconnect.
  6. One historical eth_getBalance at an old block if you need archive, and one eth_getLogs filter in the range your indexer uses.

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 or historical access if you need it, and quota. At the time of writing those are Infura, Ankr, Alchemy, DRPC, Chainstack, and Dwellir.

Dwellir's row is a keyed Celo mainnet archive endpoint over HTTPS and WSS, billed as one response credit per call on every method, which covers the historical reads Forno does not serve and keeps a mix of cheap reads and heavier log or trace calls easy to budget. 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 before assuming a wide filter will pass on every URL.

Practical split for a wallet stack

  • User-facing Celo network row - chain ID 42220 / 0xa4ec, CELO symbol, Celoscan, RPC you probed today.
  • Celo Sepolia row - chain ID 11142220 / 0xaa044c, separate config key, never reused as the mainnet RPC.
  • Fee estimation - eth_estimateGas times eth_gasPrice, no L1 fee to add, a 200 gwei floor, and the fee-currency form of eth_gasPrice when the wallet builds CIP-64.
  • Balances - one CELO row per address, read with eth_getBalance, with ERC-20 Transfer events on the CELO token treated as native moves.
  • Backend / indexer - dedicated or paid endpoint with explicit method and archive needs; do not share the browser wallet's public URL.
  • Receipt policy - latest for "included," safe or finalized only where the product needs it, and a 7-day window plus two Ethereum transactions for canonical withdrawals.

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