Network and RPC

How SOL_NETWORK picks a cluster, how to point the runtime at your own RPC endpoints, which parts of the platform hit Solana hardest, and the knobs you have when the public cluster starts rate-limiting.

6 min readUpdated 1 September 2026rpc, cluster, rate-limits, sol-network, websocket

Every other chain family in Ecosystem takes an RPC URL. Solana does not. The connection is constructed from SOL_NETWORK alone, resolved to the public cluster endpoint for that cluster, and cached on a singleton for the life of the process. This page is about what follows from that.

Cluster selection

SOL_NETWORK Cluster Endpoint the service uses
mainnet mainnet-beta https://api.mainnet-beta.solana.com
testnet testnet https://api.testnet.solana.com
anything else devnet https://api.devnet.solana.com

The third row is not a fallback you were told about. Unset, misspelled, lowercased differently, or set to mainnet-beta — which is the cluster's real name and the value most people reach for — all resolve to devnet. There is no exception thrown and no log line in the request path.

The consequence is total but invisible: addresses are generated for devnet keys, the deposit monitor subscribes to devnet, and a customer's mainnet SOL sits at an address the platform is not watching on a network it is not connected to. The funds are not lost — the keypair is the same on every cluster — but nothing will credit them until the cluster is corrected.

Run Admin → Ecosystem → Blockchains → Requirements and read the Cluster resolution line. It prints the resolved endpoint, which is the only statement in the product that cannot be misread.

  1. Expand the Solana card

The two network variables

SOL_NETWORKtype: string
Selects the cluster for every runtime operation — deposits, withdrawals, balances, mint deployment. Only "mainnet" and "testnet" are recognised; everything else resolves to devnet.
SOLANA_NETWORKtype: stringdefault: mainnet
A separate variable that selects nothing. It is stamped into the wallet address record when an address is generated, and read by nothing afterwards. Keep it equal to SOL_NETWORK.

The requirements report raises a warning when the two disagree, because the stored metadata then records a cluster the address is not on. That is a diagnosis problem rather than a money problem, but it is the kind of disagreement that makes an incident take twice as long.

Pointing the runtime at your own RPC

SOL_MAINNET_RPCtype: url
A comma-separated list of Solana RPC URLs for the cluster SOL_NETWORK resolved to — SOL_MAINNET_RPC, SOL_TESTNET_RPC or SOL_DEVNET_RPC. When set it replaces the public cluster endpoint for every runtime query.
SOL_MAINNET_RPC_FALLBACKtype: url
An optional second list, appended after the first. Same format.
SOLANA_RPC_URLtype: urldefault: https://api.mainnet-beta.solana.com
Read only by the admin token-deployment cost estimate. The runtime Solana service does not read it, and it defaults to mainnet regardless of SOL_NETWORK — on a testnet install the estimate is a mainnet figure.

The key is named for the cluster the service resolved to, not for the value you typed. SOL_NETWORK="mainnet" reads SOL_MAINNET_RPC; "testnet" reads SOL_TESTNET_RPC; anything else — unset, misspelled, mainnet-beta — resolves to devnet and reads SOL_DEVNET_RPC. Get SOL_NETWORK wrong and your paid endpoint is in a key nothing looks at, which is the same silent failure the cluster fall-through causes on its own.

Set neither key and nothing changes: the public cluster endpoint is used exactly as before. Both keys take a list, and the two lists are concatenated.

The endpoints form a failover pool, not a preference order. It dials one it has never tried before any it has measured, and after that the fastest, with a 20% margin before it switches. So every entry must be a healthy endpoint on this cluster — one bad URL in the list is not harmless padding, it is an endpoint that will be dialled.

The requirements report shows the key for the resolved cluster and whether it is set. If you leave the public cluster in place, plan capacity around it: it applies per-IP limits and returns 429 under load, and everything below exists because that is the expected condition rather than an exceptional one.

What hits Solana hardest

Four parts of the platform generate Solana RPC traffic, in roughly descending order of cost.

Transaction history. Opening a Solana transaction list fetches the last 50 signatures for the address and then fetches each transaction individually — 51 calls for one page view. Results are cached in Redis for 30 minutes per address, so the second view is free and the twenty-ninth is too. It also only parses System Program transfers, so an SPL-only address renders rows with an empty counterparty and a zero amount; that is a display limitation of this view, not a sign that the deposit was missed.

Live deposit monitoring. While a user has a deposit page open, the platform holds a WebSocket subscription for their address. Native SOL uses a logs subscription on the account; SPL uses a token-program account subscription filtered to the mint and owner. Both self-cancel after one hour of no activity, and both are torn down once a deposit is credited — the next page visit re-arms a fresh one.

The background deposit scanner. Addresses seen on a deposit page stay in a working set for 72 hours and are re-scanned on a schedule. Each Solana pass fetches the last 25 signatures for the address, or for the token account on the SPL side, and inspects the ones it has not already credited.

Withdrawals. Each one costs a fee probe, a balance read, a broadcast, a confirmation wait and then up to ten verification polls with a growing backoff. The withdrawal queue enforces a 5-second cooldown per chain between transactions, so Solana withdrawals are serialised whatever the queue depth.

The mitigations already in the code

You do not need to configure these; you need to recognise their symptoms.

  • Transaction fetches are de-duplicated and cached. Concurrent lookups of the same signature share one request, and a successful result is held for 60 seconds. Several monitors observing the same deposit therefore cost one call, not several.
  • Native deposit tracking retries up to 30 times, roughly every 5 seconds with jitter, waiting for a just-seen signature to become fetchable at finalized commitment. A deposit that takes a couple of minutes to credit under load is this working, not failing.
  • Withdrawal verification retries ten times with a 2-, 4-, 6-second … backoff capped at 10 seconds, and treats "broadcast but unverifiable" as TIMEOUT rather than FAILED. See Withdrawals and fees for why that distinction protects your float.
  • The scanner is paced by a token bucket at 0.5 Solana address scans per second by default. When more addresses are active the sweep takes longer; the request rate does not rise. Detection latency degrades gracefully instead of the whole chain 429ing.

What you can actually tune

These are Ecosystem-wide scanner settings, not Solana-specific ones, but Solana is the chain most likely to make you reach for them.

ECOSYSTEM_SCAN_RATE_SOLtype: numberdefault: 0.5
Solana-specific override for the background scanner's rate, in address scans per second. Raise it only if you have evidence the cluster tolerates it.
ECOSYSTEM_SCAN_INTERVAL_MStype: numberdefault: 120000
How often each address in the working set is re-scanned, in milliseconds.
ECOSYSTEM_SCAN_ACTIVE_TTL_MStype: numberdefault: 259200000
How long an address stays in the background working set after its last deposit-page visit, in milliseconds. The default is 72 hours.
ECOSYSTEM_BACKGROUND_SCANtype: booleandefault: true
Set to "false" to disable the background deposit scanner entirely. Deposits then rely on a live session being open.

Raising ECOSYSTEM_SCAN_RATE_SOL is the one that will bite. The default is conservative because the anonymous cluster quota is shared with every other anonymous client; pushing it produces 429s, which the scanner logs and skips past, which looks exactly like deposits going missing.

Lowering ECOSYSTEM_SCAN_ACTIVE_TTL_MS reduces load in direct proportion to how many stale addresses you stop watching, and is usually the safer lever.

Degradation, in the order you will see it

  1. Transaction history goes stale or empty first. It is the most expensive read and the most tolerant of failure, and it is cached for half an hour, so a bad minute shows up as an odd-looking list rather than an error.
  2. Live detection stops before background detection does. The subscription side is a WebSocket; the scanner side is plain HTTP. If the cluster's WebSocket endpoint is refusing you, deposits still credit — just on the scanner's cadence rather than instantly, and only for addresses that have been on a deposit page.
  3. Withdrawal confirmation gets slower, then uncertain. The retries absorb a lot. Past that, a broadcast transaction that cannot be verified is parked as TIMEOUT for a human to resolve, which is a queue that needs watching.
  4. Withdrawal broadcasts start failing. At this point the cluster is rejecting you outright and the queue backs up behind the 5-second cooldown.

If you are consistently at stage 3, you are past what this integration is sized for on the public cluster. Put a dedicated endpoint in SOL_<NETWORK>_RPC, reduce the working set, lengthen the scan interval, and be honest with yourself about Solana volume before it becomes a support queue.