Solana has no EVM-style numeric chain ID. A cluster is identified by its genesis hash, returned by getGenesisHash as a 44-character Base58 string, and the CAIP-2 chain reference is that hash truncated to its first 32 characters, giving values such as solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp for mainnet-beta. eth_chainId and getChainId answer -32601 Method not found on Solana endpoints, so the widely repeated chain ID 101 (which is actually EtherInc) is not a Solana identifier. Verifying a Solana environment means collecting the genesis hash from every endpoint, reducing each to its 32-character reference, and failing the check when the set contains more than one value.
The Short Answer: There Is No Numeric Solana Chain ID
If you are looking for the Solana chain ID, the honest answer is that Solana does not have one in the sense Ethereum developers expect. There is no chainId field in a Solana transaction, no eth_chainId method on a Solana endpoint, and no integer that the protocol mixes into signatures. The identifier that distinguishes one Solana cluster from another is the genesis hash: the Base58-encoded hash of block zero, returned by getGenesisHash.
That hash is 44 characters long. The chain-agnostic standard used by multi-chain wallets and dApps, CAIP-2, does not use it verbatim. It takes the first 32 characters as the chain reference and prefixes the namespace, producing values such as solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp for mainnet-beta. The Solana CAIP-2 namespace profile defines this as truncate(genesisHash, 32) and resolves it by calling getGenesisHash, and the CAIP-2 specification defines the general namespace:reference string format that the result slots into.
So the two values you will actually see in configuration are the cluster moniker (mainnet-beta, devnet, testnet) and the CAIP-2 chain ID (solana: plus the first 32 characters of the genesis hash). If a tutorial or a registry tells you Solana's chain ID is a number, that number is being reused from an EVM convention.
Why Solana Does Not Need a Numeric Chain ID
Ethereum needed a chain ID for replay protection. Before EIP-155, the signed fields were (nonce, gasprice, startgas, to, value, data) — nothing identified the chain, so a transaction signed for one EVM network could be replayed on another. EIP-155 included CHAIN_ID in the signing hash and encoded it into v as CHAIN_ID * 2 + 35 or CHAIN_ID * 2 + 36, making the chain ID part of the signature itself.
Solana's transaction format solves the same problem differently. A Solana transaction is a message of compiled instructions plus signatures, signed over that exact message in Solana's own wire format. It is not an RLP structure with a v value and it has no field for a numeric chain identifier. Cluster separation comes from submission instead: the transaction goes to an endpoint that participates in one cluster, and it is only valid if its recent blockhash exists in that cluster's ledger.
The consequence is practical rather than philosophical. What you get is a connection-level identifier: the genesis hash of the cluster your endpoint is actually serving. That is a stronger claim than a configured constant, because it is measured from the node instead of asserted in a config file.
The Three Clusters and Their Genesis Hashes
Solana currently publishes three public clusters, each with its own genesis hash, its own API endpoint, and a distinct purpose. The table below was produced by calling getGenesisHash against each public endpoint on 2026-10-19 and truncating the result to 32 characters to derive the CAIP-2 reference.
Note the naming. The production cluster is called mainnet-beta, not mainnet, and the moniker appears in the hostname api.mainnet-beta.solana.com. Treat mainnet alone as ambiguous in your own configuration, because a wallet or library may map it to a different string than the cluster reports.
- mainnet-beta —
5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d→solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp. Production, real SOL, real value. - devnet —
EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG→solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1. Application testing, faucet-funded, may be reset. - testnet —
4uhcVJyU9pJkvQyS88uRDiswHXSCkY3zQawwpjk2NsNY→solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z. Validator and core-release stress testing, may be reset. - The three 32-character references above match the test cases in the Solana CAIP-2 namespace profile — a useful cross-check: if your tooling produces something else for these clusters, the truncation or encoding step is wrong.
Where the Number 101 Comes From, and Why It Is Wrong
Search for a Solana chain ID and you will find claims that it is 101, sometimes 900, occasionally 501. All three are EVM chain IDs belonging to unrelated networks. On 2026-10-19 the public Ethereum chain ID registry listed 2,818 chains: 101 is EtherInc, 900 is Garizon Testnet Stage0, 501 is Columbus Test Network. None is named Solana, because Solana is not an EVM chain and never registers an eip155 chain ID.
The confusion has a predictable origin. Some wallets and SDKs accept a numeric chainId for EVM networks and a CAIP-2 string for non-EVM networks, so a few tools assigned placeholder numbers to Solana clusters to populate a single numeric field. Those placeholders are implementation details: not consensus identifiers, not stable across vendors, and two tools can legitimately disagree about which number maps to which cluster.
This is why you should never validate a Solana destination by comparing a number. The correct check is the 32-character CAIP-2 reference, or the full 44-character genesis hash, obtained from the endpoint you are about to use.
Resolving Any Endpoint to Its Chain ID
The script below takes any Solana RPC URL, asks the endpoint for its genesis hash, derives the CAIP-2 chain ID, and returns a verdict against an optional expected cluster. It also fires two negative controls, eth_chainId and getChainId, to show that Solana's JSON-RPC surface implements neither.
// solana-chain-id.mjs — Node.js 18+
// Usage: node solana-chain-id.mjs <RPC_URL> [mainnet-beta|devnet|testnet]
const [rpcUrl, expected] = process.argv.slice(2);
if (!rpcUrl) { console.error("usage: node solana-chain-id.mjs <RPC_URL> [cluster]"); process.exit(2); }
// Authoritative references: ChainAgnostic/namespaces solana caip2 => truncate(genesisHash, 32)
const KNOWN = {
"5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp": "mainnet-beta",
"EtWTRABZaYq6iMfeYKouRu166VU2xqa1": "devnet",
"4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z": "testnet",
};
async function rpc(method, params = []) {
const started = Date.now();
const res = await fetch(rpcUrl, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params }),
signal: AbortSignal.timeout(15000),
});
return { http: res.status, ms: Date.now() - started, body: await res.json() };
}
const genesis = await rpc("getGenesisHash");
if (genesis.body.error) {
console.error(JSON.stringify({ rpcUrl, error: genesis.body.error }, null, 2));
process.exit(1);
}
const fullHash = genesis.body.result; // 44 Base58 characters
const chainRef = fullHash.slice(0, 32); // CAIP-2 reference = first 32 characters
const cluster = KNOWN[chainRef] ?? "unknown";
// Negative controls: neither method exists in Solana's JSON-RPC surface.
const ethChainId = await rpc("eth_chainId");
const getChainId = await rpc("getChainId");
console.log(JSON.stringify({
rpcUrl,
httpStatus: genesis.http,
latencyMs: genesis.ms,
genesisHash: fullHash,
genesisHashLength: fullHash.length,
caip2ChainId: `solana:${chainRef}`,
cluster,
expectedCluster: expected ?? null,
verdict: !expected ? "observed" : expected === cluster ? "match" : "MISMATCH",
eth_chainId_error: ethChainId.body.error ?? null,
getChainId_error: getChainId.body.error ?? null,
}, null, 2));Measured Output from Three Public Clusters
These are the actual runs captured on 2026-10-19 against the public solana.com endpoints. Every value came back from the node during the run; the latencies are single samples, so treat them as indicative rather than as a benchmark.
$ node solana-chain-id.mjs https://api.mainnet-beta.solana.com mainnet-beta
{
"rpcUrl": "https://api.mainnet-beta.solana.com",
"httpStatus": 200,
"latencyMs": 83,
"genesisHash": "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d",
"genesisHashLength": 44,
"caip2ChainId": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
"cluster": "mainnet-beta",
"verdict": "match",
"eth_chainId_error": { "code": -32601, "message": "Method not found" },
"getChainId_error": { "code": -32601, "message": "Method not found" }
}
$ node solana-chain-id.mjs https://api.devnet.solana.com mainnet-beta
{
"rpcUrl": "https://api.devnet.solana.com",
"caip2ChainId": "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
"cluster": "devnet",
"expectedCluster": "mainnet-beta",
"verdict": "MISMATCH"
}Reading the Results: Match, Mismatch, and Unknown Cluster
The second run is the one that matters. A devnet endpoint asked to prove it is mainnet-beta returns a valid genesis hash, a valid CAIP-2 string, HTTP 200 and no error at all; every check except the comparison passes. Only the MISMATCH verdict catches the problem, which is why a health check that stops at getHealth is not enough: a devnet node is perfectly healthy, just on the wrong ledger.
An unknown cluster verdict is a different signal: the endpoint answered getGenesisHash but returned a reference that is not one of the three public clusters. That can be legitimate, since local solana-test-validator instances, private clusters and forks all have their own genesis hash. Treat unknown as untrusted input and decide deliberately whether to accept it.
The two -32601 Method not found responses are the empirical answer. eth_chainId is an EVM method and getChainId is not part of Solana's JSON-RPC interface, so a Solana node cannot produce a numeric chain ID from them. A tool that reports one anyway is reading a local constant or translating through a CAIP-2 mapping.
Verifying a Whole Environment Before You Trust It
In production you rarely verify one endpoint; you verify a pool or the endpoints embedded in a mobile build. Collect the set of distinct CAIP-2 references across all endpoints and assert the set has exactly one member. Anything else is a configuration defect that will eventually send a signed transaction to the wrong cluster.
On the client side, compare the full 32-character reference returned by the wallet or RPC endpoint against the one you expect. Never compare a short prefix: solana:5ey and solana:5ez are different strings, and prefix matching is how a mainnet-only action ends up offered on a test cluster.
- Collect
getGenesisHashfrom every endpoint in the list, not just the first one that answers. - Reduce each result to the 32-character reference and group by reference, not by hostname or by label in the config.
- Fail the deploy if the set size is greater than one, and name every endpoint that disagrees.
- Record the reference alongside the endpoint in your service catalogue, because DNS names and load balancers can be repointed without notice.
- Re-run the check on a schedule. Devnet and testnet can be reset, and a reset changes the ledger without changing the hostname.
Wallet and dApp Integration Rules for Non-EVM Chains
CAIP-2 exists because a single numeric field cannot describe every ecosystem. Its syntax is namespace:reference, and each namespace defines how the reference is resolved: for eip155 it is the decimal chain ID from eth_chainId, for solana the truncated genesis hash from getGenesisHash. A multi-chain wallet that stores only numbers cannot represent Solana correctly.
In practice, carry the CAIP-2 string as the primary key for a network and derive any legacy numeric field as a display label only. Network switches become string comparisons on the reference; logs should include it; caches should be keyed by it so mainnet and devnet responses cannot collide.
Adopt one more habit: store the full 44-character genesis hash for reconciliation and analytics, and the 32-character reference for wire-format identification. The reference is a truncation, so it is a convention for identification, not a cryptographic commitment, and keeping the full hash means a future truncation scheme cannot silently collapse two chains into one key.
What to Do When an Endpoint Cannot Answer
If getGenesisHash fails, do not fall back to a configured number or to a hardcoded cluster label. Distinguish the failure modes instead, because they mean different things for your application.
If you need a verified endpoint rather than a public one, the Solana RPC endpoints guide covers selection and provisioning, and the Solana network page describes the network at a higher level. This article deliberately does not recommend providers: which endpoint to use is a service decision, while how to prove which chain it serves is a protocol question.
- Connection refused or timeout — the endpoint is down or the network path is blocked. Try the next endpoint in the pool, but still verify its genesis hash before using it.
- HTTP 429 or 403 — you are rate limited or blocked. This is an access problem, not an identity problem; the endpoint's cluster is still unknown to you until a request succeeds. See Solana RPC rate limits and 429 errors for the retry behaviour.
-32601 Method not found— the endpoint is not a Solana JSON-RPC node at all, or is a proxy that only forwards a subset of methods. Do not assume its cluster.- Valid result with an unexpected reference — the endpoint is answering, but for a different cluster than you expected. Treat this as a
MISMATCHand stop.
Limitations and Assumptions
The Solana CAIP-2 namespace profile is marked as a draft, last updated March 2023, so the truncation rule is a de facto convention rather than a finalised standard. Implementations agree on it today and the three references here match the profile's own test cases, but re-check the specification rather than treating it as frozen.
The latencies above are single samples from one machine. They show the call is cheap, not that an endpoint is fast; public endpoints are rate limited and change without notice, which is why production needs verification plus a private endpoint.
Cluster resets are also real. Devnet and testnet are documented as subject to ledger resets, and a reset can change the genesis hash. Treat any hardcoded non-production hash as cache with an expiry, refreshed from the endpoint, never a permanent constant.