0G RPC: A Developer's Guide (Chain ID 16661)
0G is the execution layer of an AI-focused stack: storage for datasets and model artefacts, a data-availability layer, and a compute marketplace. The pitch suggests exotic infrastructure, but the chain itself is ordinary EVM. Solidity contracts, ABI encoding, and viem or ethers code work unchanged.
The differences are in the details. On 0G the base fee is 7 wei but the tip floor is 4 gwei, the default RPC route can land on a pruned node, eth_getLogs is capped by results rather than blocks, and two precompiles have no bytecode at all. Each of those can break code that was written for Ethereum and never tested here.
Everything below was measured on October 21, 2026, through the SwiftNodes endpoint and the chain's public backends.
The endpoint
https://rpc.swiftnodes.io/rpc/0g?key=YOUR_API_KEY
wss://rpc.swiftnodes.io/ws/0g?key=YOUR_API_KEY
The chain slug is 0g, not zerog, and the gas token is 0G with 18 decimals. If your library does not ship a preset, define it with id 16661, native currency 0G and the HTTP URL above.
What we measured
| Property | Value |
|---|---|
| Chain ID | 16661 (0x4115) |
| Client | reth/v1.8.1 (official), Geth/v1.15.11 (publicnode), Geth/v10.0.0/drpc |
| First block | 2025-08-23 01:19:05 UTC |
| Average block interval | 0.8402 s over 100,000 blocks; 0.8361 s over 300 |
| Transactions per block | 0.233 in the sample; 246 of 300 blocks were empty |
| Block gas limit | 36,000,000 |
| Base fee | 7 wei in every block sampled |
eth_maxPriorityFeePerGas |
4 gwei |
eth_gasPrice |
4.000000007 gwei |
| Plain 0G transfer | 21,000 gas, 0.000084 0G |
eth_getLogs cap |
20,000 results per query |
finalized tag |
1 to 28 blocks behind, depending on the backend |
| Archive | official keeps it; publicnode is pruned |
| 0G precompiles | 0x…1000 DASigners, 0x…1002 WrappedOGBase |
The mix matters: these backends differ on archive depth, log filters and finality tags.
The fee market is a 4 gwei floor
0G's fee market is closer to a fixed fee than an auction. The base fee was 7 wei in every block we sampled. eth_maxPriorityFeePerGas returned 4 gwei, and eth_gasPrice returned 4.000000007 gwei, which is the priority fee plus the base fee.
A type-2 transaction shows the same numbers: maxPriorityFeePerGas 4 gwei, maxFeePerGas 4.000000009 gwei, effectiveGasPrice 4.000000007 gwei. A legacy type-0 transaction paid the same effective price.
So a plain transfer at 21,000 gas costs 0.000084 0G, and the 410,742-gas 0G Storage transaction we sampled cost 0.00164 0G. The base fee can rise with demand, but at current usage it is noise next to the tip.
Do not send a zero tip because the base fee is 7 wei, and do not hardcode 1 gwei from an Ethereum example. Read eth_gasPrice or eth_maxPriorityFeePerGas, then estimate. More on the general pattern in estimating gas with eth_estimateGas.
Fast blocks, thin traffic
0G produces a block every 0.84 seconds, and most of them are empty. In a 300-block sample there were 70 transactions, or 0.23 per block; 246 blocks had none. The blocks used 0.19% of their 36 million gas limit. 64 transactions were legacy type 0 and 6 were type 2, sent by 22 addresses to 20 recipients. 18 different block producers signed the blocks.
The busiest contract was the 0G Storage Mine contract at 0xcd01c5Cd953971CE4C2c9bFb95610236a7F414fe, with 24 of the 70 transactions. The sample transaction 0xb6e902d2… carried 10.8 KB of calldata to that contract, used 410,742 gas and emitted three logs. A prestate trace showed it touching the Storage Flow, Mine and Reward contracts. That is what most 0G activity looks like: storage proofs and mining, not token swaps.
So a one-second polling loop will mostly fetch empty blocks. Use WebSocket newHeads, or batch reads with Multicall3. Do not infer congestion from gasUsed; the chain is fast and mostly idle.
Two precompiles that are not contracts
0G adds two precompiles beyond the Ethereum set:
| Address | Name | What it does |
|---|---|---|
0x0000000000000000000000000000000000001000 |
DASigners | Queries the data-availability signer and quorum state |
0x0000000000000000000000000000000000001002 |
WrappedOGBase | Mints and burns the backing for W0G |
Both return 0x from eth_getCode. There is no bytecode to inspect, but calls to them work, so the common "zero code means EOA" check gives the wrong answer.
The calls are easy to verify. Calling getWA0GI() on 0x…1002 returns the W0G token address, 0x1Cd0690fF9a693f5EF2dD976660a8dAFc81A109c.
params() on the DASigners precompile returned 30 tokens per vote, 6,400 maximum votes per signer, 10 maximum quorums, 1,800 blocks per epoch (about 25 minutes at 0.84 s blocks) and 3,072 encoded slices per DA blob.
W0G is the ERC-20 form of the native token. At 0x1Cd0690fF9a693f5EF2dD976660a8dAFc81A109c it reports the name Wrapped 0G, the symbol W0G, 18 decimals and a total supply of 33,436,632.685882665. Its native 0G balance equalled that supply exactly at the time of measurement, which is the on-chain backing.
Do not call 0x…1002 directly to wrap or unwrap; the precompile is for the W0G contract. If your app bridges, check which W0G contract the venue expects; the CCIP and older LayerZero deployments are different.
Finality tags depend on the backend
0G uses CometBFT, and the docs describe sub-second finality. The JSON-RPC tags are less consistent. In six samples through the load-balanced route, finalized trailed the head by 1 to 28 blocks, and safe sometimes trailed by more than finalized. The official reth node reported both 6 blocks behind; publicnode reported them at or near the head; dRPC had the widest spread.
That is a backend artifact, not a consensus change. An indexer that waits for finalized may wait 1 block on one request and 28 on the next, and the safe and finalized labels can invert. Pick one: wait a fixed 30 blocks (about 25 seconds), pin a single backend, or treat finalized as a hint rather than a cross-provider guarantee.
For how the tag behaves on other chains, see the finalized block tag across 62 chains and handling chain reorgs in an indexer.
The default route is pruned
0G's public backends do not all keep full history. The official node answered contract code at blocks 1,000,000 and 40,000,000; publicnode returned missing trie node for the same requests.
That is what a load-balanced route looks like without an archive flag: one request succeeds, the next fails. On SwiftNodes, add &archive=1 on a paid plan to restrict routing to archive nodes:
https://rpc.swiftnodes.io/rpc/0g?key=YOUR_API_KEY&archive=1
We verified code at blocks 1,000,000 and 40,000,000 through that route. W0G's native balance at block 40,000,000 was 14,605,412.38 0G; today it is 33.4 million. The free tier does not include archive routing, so a historical read without the flag is not deterministic.
For the general distinction, see full node vs archive node; the archive switch is documented in our API reference.
Logs: 20,000 results, not blocks
The official node caps eth_getLogs by result count. A 10,000-block query returned 4,930 logs. A 50,000-block query failed with:
query exceeds max results 20000, retry with the range 47768080-47814930
The error includes the range the node is willing to serve. Use it. The cap is on results, so a wide range with a narrow address filter can pass while a short range over a busy contract fails.
One public backend also refuses unfiltered queries:
Please specify an address in your request
Always pass an address or a topic filter on 0G. If you need every log in a range, page by blocks and shrink the step when the result cap is hit.
for (let from = start; from <= end; ) {
const to = from + step - 1n > end ? end : from + step - 1n;
try {
await client.getLogs({ address, fromBlock: from, toBlock: to });
from = to + 1n;
} catch {
step /= 2n; // the error names a range; halve until it fits
}
}
The SwiftNodes free tier adds its own limit: 100 blocks per eth_getLogs request. Paid plans remove that block-span cap and leave the upstream result cap. For a comparison with other chains, see eth_getLogs range caps.
eth_getBlockReceipts and eth_getProof also work; see eth_getProof for the verification pattern.
Tracing and simulation
| Method | Result on 0G |
|---|---|
eth_getBlockReceipts |
works |
eth_getProof |
works |
debug_traceTransaction |
works with a tracer; {} is rejected |
debug_traceCall |
works with a tracer |
debug_traceBlockByNumber |
works with a tracer |
trace_block, trace_transaction |
not available |
txpool_status |
not reliably available |
eth_simulateV1 |
works |
WebSocket newHeads |
works |
The tracer requirement is the one that catches people. A standard geth node defaults to the struct logger when you pass {}. 0G's geth backend rejects an empty tracer object:
Tracer object with 'tracer' param is required for debug_traceTransaction
Pass one explicitly, for example {"tracer":"callTracer"}, as the second parameter. callTracer and prestateTracer both worked. The prestate tracer showed the Flow, Mine and Reward contracts on the storage transaction above.
Debug and trace calls need a paid plan on SwiftNodes. The trace_ namespace is not served by any 0G backend we tested, and txpool_status is missing or returns an empty object.
WebSocket newHeads is the reliable live feed. The socket opened in 18 ms and delivered a header immediately. The free tier serves newHeads only; a logs subscription is refused.
Summary
- 0G is an EVM L1 with chain ID 16661, 0.84-second blocks, and 0G as an 18-decimal gas token.
- The base fee is 7 wei, but the tip floor is 4 gwei. A plain transfer costs 0.000084 0G.
- Most blocks are empty. The busiest contract in our sample was 0G Storage's
Minecontract. 0x…1000and0x…1002are live precompiles with no bytecode. W0G lives at0x1Cd0690fF9a693f5EF2dD976660a8dAFc81A109c.finalizedis backend-dependent, the default route is pruned, andeth_getLogsis capped at 20,000 results. Wait a fixed number of blocks, use&archive=1for history, and filter logs by address.
0G endpoints and method support are on our 0G RPC page, alongside every other chain on the same key. Start free if you want to test the endpoint with your own key; no card is required.
John Sullivan covers RPC infrastructure, node operations, and multi-chain development at SwiftNodes — what it actually takes to keep endpoints fast, fresh, and reliable across EVM and non-EVM networks.
Related posts
- Etherlink RPC: A Developer's Guide (Chain ID 42793)
Etherlink is the EVM rollup on Tezos. Measured: sub-second to 1.5 s blocks, a fixed 1 gwei base fee, plain transfers that use 621,000 gas because the data fee is charged as gas, a 500-block eth_getLogs cap, and no safe tag.
- Bitlayer RPC: A Developer's Guide (Chain ID 200901)
Bitlayer is an EVM chain that pays gas in BTC. Measured: 3-second blocks from 21 rotating validators, a hard 0.025 gwei minimum tip, a transfer that costs 52.5 satoshis, no safe or finalized tags, and a public RPC that keeps only recent state.
- Tempo RPC: A Developer's Guide (Chain ID 4217)
Tempo is a payments chain with no native token: fees are paid in USD stablecoins. Measured: 550 ms blocks, instant finality, an eth_getBalance that returns the same huge number for every address, the 0x76 transaction type, and a 20,000-result eth_getLogs cap.