Skip to content

Networks and bring-your-own RPC endpoints

Bring-your-own endpoints, how resolution picks one, and reading the x-rpc-source header.

Updated View as Markdown

The networks a workspace starts with

Every workspace’s registry is seeded from the same ten built-in networks:

Slug Network VM Chain ID
ethereum Ethereum evm 1
base Base evm 8453
bnbchain BNB Chain evm 56
arbitrum Arbitrum evm 42161
optimism OP Mainnet evm 10
sepolia Sepolia evm 11155111
arbitrum-sepolia Arbitrum Sepolia evm 421614
solana Solana svm 101
solana-testnet Solana Testnet svm 102
solana-devnet Solana Devnet svm 103

These slugs are what every network selector and defaultNetwork accepts. A well-formed slug that is not one of them — or one your operator removed — is a 404, not a 400.

Use a testnet while you are integrating. sepolia and solana-devnet carry no real value, and every example in these docs uses ethereum only because it is the obvious name. A first transaction on ethereum is a real mainnet transaction.

TEE Docker ships no RPC service. Anything that touches a chain — building, simulating, submitting, or checking a transaction — runs against an endpoint you or your operator supplied.

Resolution

An endpoint can come from three places. They are not searched in turn at request time — the workspace’s own network registry is read once, and each entry already carries the provenance it was given:

The workspace registry — authoritative

What a route actually reads. PUT /v1/workspace/networks/{slug} writes straight into it, and is how an endpoint changes once a workspace exists.

The tenant rpc map — a seed, not a lookup

Your operator’s map is copied into the registry when the workspace is created, over the built-in defaults. That is the only moment it is read.

A wative-core built-in — permitted, not searched

Networks your operator never configured keep the URL wative-core ships. That entry is usable only where allowDefaultRpc permits it, and permission is checked on every call, not once at creation.

So a registry entry is necessary but not sufficient. A call fails with 409 and rpc_not_configured in two cases: the network has no URL at all, or it has a built-in one and allowDefaultRpc is off.

Provenance is about how an endpoint was set, not what it points at. An explicit PUT or an operator seed counts as tenant even when its URL happens to equal the one wative-core ships.

Every answer says where its endpoint came from

Resolution is never ambiguous. The reported source is one of three values:

rpcSource Meaning
tenant A workspace-registered or tenant-configured endpoint answered
builtin wative-core’s shipped default answered — an endpoint nobody here chose
none Nothing usable is configured for this network

GET /v1/workspace/networks reports it per network, and is the only place none appears — it is a description of configuration, not of a call.

The four transaction routes stamp the source on the response as the x-rpc-source header, so “why is this slow” or “which endpoint actually served this” is answerable from the response alone. The header only ever reads tenant or builtin: a network that would resolve to none fails with 409 before a response exists to stamp.

When a route needs an endpoint and resolution returns none, the call fails with 409 and code rpc_not_configured.

Setting an endpoint

curl -X PUT "$API_URL/workspace/networks/ethereum" \
  -H "Authorization: Bearer $TOKEN" \
  -H "content-type: application/json" \
  -d '{"rpcUrl":"https://your-endpoint.example.com/eth"}'

The URL is admitted through a boundary check before it is stored — an endpoint the service refuses to accept returns 400 with rpc_endpoint_rejected.

What the boundary accepts

Every rule below produces that same 400, so it is worth knowing them before you debug one:

Rule Rejected
Scheme must be https: Any http:// endpoint
No credentials in the authority https://user:pass@host/… — put an API key in the path or query instead, which is allowed
No fragment https://host/rpc#x
Host must resolve to a globally routable address localhost, 127.0.0.1, 10.x, 172.16–31.x, 192.168.x, 169.254.x, CGNAT, and the IPv6 equivalents
Host must resolve at all A name with no A or AAAA record
At most 2048 bytes A longer URL

There is no port restriction — a non-standard port is fine.

The name is resolved when you register it and on every request that uses it, and the resolved address is pinned for the connection. So an endpoint that starts resolving to a private address later begins failing, even though it was accepted at registration. The update is applied transactionally: if the new endpoint cannot be resolved after being written, the previous one is restored rather than leaving the workspace pointing at something unusable.

Requires the write scope. Reading the list requires read.

Failures you should expect

Chain-touching routes surface transport problems distinctly from on-chain outcomes:

Code Status Meaning
rpc_not_configured 409 No usable endpoint for that network
rpc_unreachable 502 The endpoint did not respond, or answered non-2xx
rpc_rejected 502 The endpoint responded with a refusal
rpc_endpoint_rejected 400 The URL you supplied was refused at admission
rpc_capacity_exceeded 429 Too many concurrent chain operations
unsupported_network 400 The signing engine cannot work with that network
account_not_found 404 The ?network= selector named a network this workspace does not define

A simulation that fails on-chain is a 200 with success: false and a revert reason. A simulation that could not complete at all is a transport error, and is never rendered as 200.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close