# Chains


Every Somnia network as a viem `Chain`, from one import — so nothing has to
hand-roll a `defineChain({...})` or reach for `viem/chains` again.

```ts
import { somniaShannon } from "@somnia-chain/markets-sdk/chains";
```

`chain` is a required field of every exchange/client config (it signs writes and
sizes viem's waiting heuristics), and `wsRpcUrl` is the transport the live tail
runs on — both come straight off these objects.

Moving tokens _between_ these networks is the [bridge](./bridge) — same module,
same import.

## The networks

| Export          | Chain id | Native | Blocks | Multicall3 | Explorer                                                                   |
| --------------- | -------- | ------ | ------ | ---------- | -------------------------------------------------------------------------- |
| `somniaMainnet` | `5031`   | SOMI   | 100 ms | ✅         | [explorer.somnia.network](https://explorer.somnia.network)                 |
| `somniaShannon` | `50312`  | STT    | 100 ms | ✅         | [shannon-explorer.somnia.network](https://shannon-explorer.somnia.network) |
| `somniaTokyo`   | `50321`  | SOMI   | 100 ms | ✅         | —                                                                          |
| `hidekiTestnet` | `50383`  | STT    | 100 ms | ✅         | —                                                                          |
| `somniaLocal`   | `31337`  | STT    | —      | —          | —                                                                          |

- **Shannon** (`somniaShannon`) is the public testnet and the default target for
  development. `https://dream-rpc.somnia.network` is an alias for the same
  network and is listed as a secondary endpoint.
- **Somnia Tokyo** (`somniaTokyo`) is the Tokyo network on mainnet. It
  launched as Hideki Mainnet. Its native token is SOMI, the same coin as Somnia
  mainnet. Somnia mainnet and Somnia Tokyo are the two ends of the mainnet
  [bridge](./bridge) lane. Its definition also names `rpcUrls.send` and
  `rpcUrls.session`, which reach a validator directly. The SDK sends
  transactions and session calls there: see
  [Send and session endpoints](./reference/networks-and-endpoints#send-and-session-endpoints).
- **Hideki Testnet** (`hidekiTestnet`) is the low-latency Tokyo testnet. If you
  are measuring latency, measure here. Somnia Tokyo and Hideki Testnet commit
  transactions every 10 ms and make a ledger block every 100 ms: see
  [Somnia Tokyo and Hideki Testnet block time](#somnia-tokyo-and-hideki-testnet-block-time).
  Its definition also names `rpcUrls.send` and `rpcUrls.session`, which reach a
  validator directly. The SDK sends transactions and session calls there: see
  [Send and session endpoints](./reference/networks-and-endpoints#send-and-session-endpoints).
- **`somniaLocal`** is the anvil stack (`demo-clob.sh up`, `DEPLOY_ENV=local`).
  It deliberately presents as _Somnia_/_STT_ rather than _Foundry_/_Ether_: the
  agent-facing docs read the chain name and native symbol out of this object, and
  a local stack should still read as Somnia there.

Every definition carries **both** transports — `rpcUrls.default.http` and
`rpcUrls.default.webSocket` — so an exchange can be built from the chain alone:

```ts
import { SomniaMarkets } from "@somnia-chain/markets-sdk";
import { hidekiTestnet as chain } from "@somnia-chain/markets-sdk/chains";

const exchange = new SomniaMarkets({
  chain,
  wsRpcUrl: chain.rpcUrls.default.webSocket[0],
  indexerUrl: "/v1/graphql",
});
```

## Somnia Tokyo and Hideki Testnet block time

Somnia Tokyo and Hideki Testnet run the Kenneth consensus layer. Kenneth makes a
PBFT block every 10 ms and commits each transaction in one of these blocks. Each
chain makes a ledger block every 100 ms, as Somnia mainnet and Shannon do. So a
transaction is committed before the ledger block that holds it exists.

The RPC serves only ledger blocks. Block numbers, logs, receipts and the
`latest` and `pending` tags all count ledger blocks. So `blockTime` is `100` in
both definitions. Use 100 ms when you change a time into a number of
blocks. The 10 ms sets how soon a transaction is committed, not how often a
block appears in the RPC.

Do not set `blockTime` to `10`. From viem 2.31, `blockTime` sets the default
polling interval, and that interval is never shorter than 500 ms. So `10` does
not make a client faster. But code that changes a time into a number of blocks
gets a count that is 10 times too large.

## Absences are deliberate

The local chain carries **no `contracts.multicall3`** — nothing is deployed
there to point at. That is not an oversight to be filled in with the canonical
`0xcA11bde05977b3631167028862bE2a173976CA11`: viem routes batched reads at
whatever address it finds, so a Multicall3 entry with no code behind it turns
every batched read into a failure. An absent field just makes viem fall back to
individual `eth_call`s. In the same way, only mainnet and Shannon carry
`blockExplorers`: no public explorer is known for Somnia Tokyo or Hideki
Testnet.

Each network's Multicall3 address is its own, so don't copy one to another:

- Mainnet and Shannon have **their own** deployments at different addresses,
  `0x5e44…5a11` and `0x841b…4223`. The presigned "Nick's method" deployment can
  never land there: contract creation costs ~4,928 gas per deployed byte on
  Somnia, far over that transaction's fixed 500k limit, and its nonce-0
  deployer has already been burned on these networks.
- Somnia Tokyo and Hideki Testnet have Multicall3 at the **canonical**
  `0xcA11…CA11`, because each genesis puts it there. Somnia Tokyo has code there
  from block 0. Hideki Testnet has code from block 1, since its 2026-09-17
  regenesis.

The values here — ids, cadence, Multicall3 addresses — were verified against the
live networks (`eth_chainId`, block-timestamp deltas over 10k blocks, and a real
`getBlockNumber()` call on each Multicall3 candidate), not copied from a chain
list.

## Resolving a chain id you were handed

Deployment manifests (and `NEXT_PUBLIC_CHAIN_ID`-style env) carry a plain
`number`. `getSomniaChain` bridges that to a `Chain`, returning `null` when the
id isn't one of ours:

```ts
import { getSomniaChain, defineChain } from "@somnia-chain/markets-sdk/chains";

const chain =
  getSomniaChain(deployment.chainId) ??
  defineChain({
    id: deployment.chainId,
    name: "Somnia",
    nativeCurrency: { name: "STT", symbol: "STT", decimals: 18 },
    rpcUrls: { default: { http: [rpcUrl] } },
  });
```

`somniaChains` is the same table, keyed by id — iterate it to build a network
switcher. `isSomniaChainId(id)` is the type guard that narrows a `number` to
`ChainId`, enough to index it without a cast. When the id flows the other way —
your code names the network — **`ChainId`** is the constant to reach for:
`ChainId.somniaShannon` is `50312`, keyed by the same export names as the
definitions (`ChainId.somniaShannon === somniaShannon.id` by construction).

## Extending one

`defineChain` and the `Chain` type are re-exported here, so overriding an
endpoint (a private node, a fork) needs no second import:

```ts
import { defineChain, somniaShannon } from "@somnia-chain/markets-sdk/chains";

const forked = defineChain({
  ...somniaShannon,
  id: 31_337,
  name: "Somnia Testnet (forked)",
  rpcUrls: { default: { http: ["http://127.0.0.1:8545"], webSocket: ["ws://127.0.0.1:8545"] } },
});
```

## Relationship to `viem/chains`

viem ships two of these networks as `somnia` and `somniaTestnet`; our
`somniaMainnet` and `somniaShannon` carry the same content under the names the
runbooks use (a test asserts field-for-field parity), and the module adds the
three networks viem cannot ship — Somnia Tokyo, Hideki Testnet and the local
stack are ours, on our cadence. One import covers every target, and a new network
lands when we bring it up rather than when viem next cuts a release.

Each definition lives in its own file under `src/chains/definitions/`, written the
way viem writes its own, so the two stay diffable. `src/chains/bridge/` sits
alongside them with the warp-route registry — see the [bridge guide](./bridge).

---

Every page is also Markdown: append `.md` to its URL, or ask for `text/markdown`. Found something wrong, missing or confusing in these docs? Tell us: `POST /api/feedback` on this site with JSON `{"message": "…", "context": "what you were doing and what happened", "page": "…"}`, or a plain-text body. `GET /api/feedback` gives the JSON Schema. The team reads every one.
