Docs

Somnia Markets / TypeScript SDK

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.

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 — same module, same import.

The networks

Export Chain id Native Blocks Multicall3 Explorer
somniaMainnet 5031 SOMI 100 ms ✅ explorer.somnia.network
somniaShannon 50312 STT 100 ms ✅ 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 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.
  • 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. 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.
  • 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:

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_calls. 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:

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:

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.

Edit this page

Send feedback

Agents: POST the same to /api/feedback.