Somnia Markets / Reference
Configuration
This page lists every field a SomniaMarkets instance accepts, with its type, whether it is required, and its default. Engine fields form ClientConfig; signer fields and rpcWebSocketConstructor belong to the configured root. For the values to put in these fields on each network, see Networks and endpoints.
import { SomniaMarkets } from "@somnia-chain/markets-sdk";
const exchange = new SomniaMarkets(config); // config: SomniaMarketsConfig
SomniaMarketsConfig
SomniaMarketsConfig accepts one chain configuration or a chains list, plus the signer fields privateKey, account, and walletClient, and the optional root RPC constructor.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
indexerUrl |
string |
no | none | Envio/Hasura GraphQL endpoint over HTTP. A same-origin relative path is accepted in a browser. Omit for a chain-only client (smart accounts, executors, on-chain reads): every indexer read and live watch then throws NotConfiguredError naming this field. |
indexerWsUrl |
string |
no | indexerUrl with http replaced by ws |
The indexer's GraphQL WebSocket endpoint, for live account views (client.accountOrders.subscribe). Set it when indexerUrl is a relative path, or when subscriptions are served from another address. Without it and without an absolute indexerUrl, subscribe throws NotConfiguredError naming this field. |
chain |
Chain (viem) |
yes | none | The chain the markets live on. |
wsRpcUrl |
string |
no | chain.rpcUrls.default.webSocket[0] |
The WebSocket RPC endpoint. It is the only chain transport: subscriptions, reads, and writes all use it. There is no HTTP fallback. Required when chain carries no WebSocket URL. |
rpcWebSocketConstructor |
SomniaMarketsWebSocketConstructor |
no | managed ws on Node; native/isows in browsers |
Standard RPC constructor selected once per exchange, shared as a class across its chain engines. Each engine owns a separate created socket. Also opens price-feed sockets; indexer HTTP reads do not use it. |
indexerHeaders |
Record<string, string> |
no | none | Extra headers on every indexer request, for example a Hasura admin secret. Server-side only. |
signal |
AbortSignal |
no | none | Cancels in-flight indexer reads and receipt confirmations. Client-wide, not per read. An indexer abort rethrows the caller's reason, not an IndexerError. A local or wallet write canceled before broadcast rethrows that reason. After broadcast, canceled confirmation throws TransactionOutcomeUnknownError with the transaction hash and the reason in cause. Check that hash before retrying the write. Other chain reads have their own 4-second request timeout. |
fees |
FixedFees |
no | DEFAULT_FEES |
Fixed EIP-1559 fees for SDK-signed writes. |
addresses |
SomniaMarketsAddresses |
no | {} |
Protocol contract addresses. Every entry is optional. A method that needs a missing address throws NotConfiguredError when it is called. |
priceFeed |
PriceFeedConfig |
no | none | The realtime price-feed endpoint. Required only by the price methods that fetch or watch (watchPrice, fetchPrice, fetchPriceOHLCV, and the client's fetchPrice* and listPriceFeeds). Its quote is mandatory to watch: an unpinned feed rejects watchPrice/watchPrices with NotConfiguredError because the tick tape's cursor is unique only within one quote; the one-shot reads still work unpinned. The getLivePrice* reads return null or [] until a watch is open. |
debug |
(event: DebugEvent) => void |
no | none | Receives structured debug events. When unset the SDK emits nothing and does no debug-only work. |
privateKey |
`0x${string}` |
no | none | A local signing key. The SDK signs locally and confirms in one round-trip. |
account |
Account | Address |
no | none | A viem account, or a plain address to trade as through walletClient. |
walletClient |
WalletClient |
no | none | An external signer, for example an injected browser wallet. |
Counts and indexerHeaders
This section describes how the count methods (countMarkets, countOrders and the others) pick a query. A count first asks Hasura for <Table>_aggregate. Only a privileged role, such as the one an admin secret in indexerHeaders selects, has that field. On the public role Hasura rejects the field, and the count scans at most 10,001 ids instead.
The client remembers that rejection for five minutes. The memory is per client, endpoint, header set and table. In that window a count of the same table sends only the scan. The first counts of a table that run at the same time share one probe. Only Hasura's exact field '<Table>_aggregate' not found in type: 'query_root' error is remembered. Any other failure still throws and is not remembered. stopLive() clears the memory. After a role gains or loses the field, counts use the new permission within five minutes, or at once after stopLive().
RPC constructor selection
Node connections use the SDK-managed ws transport by default. Replacing
globalThis.WebSocket or isows on Node does not select an RPC constructor.
Price-feed sockets use the same selection and the same explicit constructor.
Browsers retain their native/global/isows selection, including replacements
installed before or after SDK import. An explicit rpcWebSocketConstructor
selects the constructor on either platform, independent of import order.
The selected constructor receives only standard WebSocket arguments. RPC
construction calls new Constructor(url). Price-feed construction calls
new Constructor(url, "graphql-transport-ws"). A protocol is always the
standard second string or string-array argument. The SDK retains the exact
returned socket and does not mutate the caller's constructor or prototype.
Constructor selection is lazy. Closing the owner during dependency loading
prevents construction, and an indexer-only exchange makes no RPC dial.
A supplied constructor is trusted infrastructure. Every created socket belongs
to this exchange and must promptly dispose connecting and open work through
close(), stop replacement dials and safely observe errors during retirement.
Sharing a class between exchanges must not share its created sockets. A standard
interface proves the calling convention, not physical disposal. Node 22's native
WebSocket does not satisfy this custom disposal obligation; use the managed
Node default or a conforming implementation.
Migrating a Node override
Replace global assignment with a per-owner input. AppWebSocket below is your
application's conforming constructor; config is its existing exchange config.
// Before
globalThis.WebSocket = AppWebSocket;
const exchange = new SomniaMarkets(config);
// After
const exchange = new SomniaMarkets({
...config,
rpcWebSocketConstructor: AppWebSocket,
});
A compile-checked factory can make that ownership explicit:
import {
SomniaMarkets,
type SomniaMarketsConfig,
type SomniaMarketsWebSocketConstructor,
} from "@somnia-chain/markets-sdk";
function createExchange(config: SomniaMarketsConfig, AppWebSocket: SomniaMarketsWebSocketConstructor) {
return new SomniaMarkets({ ...config, rpcWebSocketConstructor: AppWebSocket });
}
Modeled operations preserve concrete SDK constructor/member failures by identity;
other transport failures become RpcError with the original value as cause.
Price watches follow the same rule when a price-feed socket cannot be selected
or constructed: watchPrice and watchPrices reject with the constructor's SDK
error, or with RpcError for any other thrown value.
Root validation rejects a non-function input with InvalidInputError. Unexpected
configuration-access failures become InvariantError with their original cause.
close() and the engine's stopLive() finish remaining cleanup before reporting
the first disposal failure. A disposal failure following an already delivered
operation timeout does not replace that timeout; it is retained for owner cleanup.
A constructor that violates the disposal obligation cannot receive a physical
release guarantee from the SDK. Undecorated viem operations retain their platform
operational contract after access.
Signer resolution
A method that writes, or reads data scoped to the caller, resolves the caller's address in this order: walletClient.account.address, then account.address, then account when it is a string, then the address derived from privateKey. When none is set, walletAddress is undefined and every authenticated method throws SignerRequiredError.
setSigner(signer) replaces the signer after construction. Passing {} removes it. Live watches and market data are unaffected.
Signer paths
| Aspect | privateKey or a signing account |
walletClient |
|---|---|---|
| Who signs | The SDK, locally | The wallet, after a user prompt |
| Fees and gas | The fixed values from fees and the gas ceiling |
The same fixed values, passed to the wallet as maxFeePerGas, maxPriorityFeePerGas, and gas |
| Send path | eth_sendRawTransactionSync: send and receipt in one round-trip; falls back to eth_sendRawTransaction when the node does not serve it |
eth_sendTransaction through the wallet; the receipt is read on heads and after 1 s idle, within a 60 s total confirmation deadline |
| Reverts | Decoded by replaying the failed call at the receipt's block | Surfaced by the wallet's simulation before the prompt, with revert data |
| Approvals | One approve(maxUint256) per token and spender, on the first order that needs it, cached for the trader's lifetime |
The same, as a second wallet prompt |
| Nonce | Tracked locally after one fetch; reset on a rejected send | Managed by the wallet |
Lazy connections
The viem chain WebSocket opens on the first chain read, write, or watch. An instance that only performs indexer reads never opens one. The price-feed WebSocket opens on the first watchPrice. close() stops the watches and channels and closes every socket the instance opened, both the price-feed WebSockets and the viem chain transport, so a Node process exits on its own afterwards. Each instance owns its own chain socket, even when several use the same wsRpcUrl, so closing one never affects another.
FixedFees
| Field | Type | Description |
|---|---|---|
maxFeePerGas |
bigint |
Fee ceiling in wei per gas. The unspent margin above base fee plus tip is refunded. |
maxPriorityFeePerGas |
bigint |
Tip in wei per gas. |
DEFAULT_FEES is { maxFeePerGas: 60_000_000_000n, maxPriorityFeePerGas: 0n }: a 60 gwei ceiling and no tip.
Gas
Every SDK-signed write uses a fixed gas ceiling of 10,000,000 gas. Gas is never estimated. Unused gas is not charged. The mempool accepts a transaction only when the account holds gas × maxFeePerGas on top of the transaction value: 0.6 STT or SOMI at the defaults. A lower-level write accepts a per-call gas override only when its public parameter type declares one. Unified exchange methods such as createOrder and cancelOrder do not expose that override.
SomniaMarketsAddresses
Every field is optional and typed Address, except lend.
| Field | Used by |
|---|---|
collateral |
The venue's collateral ERC-20. faucet() mints it on test networks. |
testUsdc |
Legacy alias for collateral. collateral takes precedence. |
binaryModule |
Complete-set mint and redeem, market creation, hub-approval reads, system diagnostics. |
marketCreator |
The live tail watches MarketCreated on it to discover new binary markets. |
clobFactory |
System diagnostics fallback. |
binaryPoolImpl, binaryPoolBeacon |
Surfaced to apps. Not read by the SDK. |
binarySettlement |
redeemDirect, claimOwed, getSettlement. |
operatorPermissionsRegistry |
Operator approval of a stop-order registry. Discoverable at runtime with getOperatorPermissionsRegistry(pool). |
marketsCore |
Operator and venue reads, createOperatorAdmin. |
collateralRouter |
The native-token and Permit2 complete-set path (mintSetNative, mintSetPermit2, redeemNative). |
marketCreatorFactory, marketCreatorFactoryV2 |
createMarketCreatorAdmin. |
oracleHub |
OracleHub reads and createOracleHubAdmin. |
perpPoolFactory |
Perp discovery when the indexer has no perp rows. loadMarkets otherwise resolves the factory from MarginBank.getSystemConfig() on an indexed perp row, so this is needed only when there is no such row. Without it, discovery is SKIPPED and reports through perpDiscoverySkipped. When set it takes precedence. |
priceFeedScheduler |
The Somnia price feed that the perp pools' oracles read. accountPositions.subscribe has the node push the account's state on its price writes. Without it, marks follow a read every 5 s. The Shannon and Hideki constants set it. |
lend |
LendAddresses for the third-party SomniaLend deployment. Backs client.lend. |
fakeOracle |
resolve and voidMarket on demo stacks. |
oracleAdapterFactory, sharedOracleAdapter |
Deprecated. Kept so old configurations type-check. Not read. |
SOMNIA_MAINNET_ADDRESSES, SOMNIA_TESTNET_ADDRESSES and SOMNIA_HIDEKI_TESTNET_ADDRESSES, exported from the root entry, hold the current deployments. SOMNIA_MAINNET_LEND and SOMNIA_TESTNET_LEND hold the SomniaLend addresses and are already included in the mainnet and testnet constants. Hideki has no SomniaLend deployment, so its preset omits lend.
PriceFeedConfig
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
url |
string |
yes | none | HTTP GraphQL endpoint of the price-feed indexer. One endpoint serves every asset. |
wsUrl |
string |
no | url with http replaced by ws |
WebSocket GraphQL endpoint for live subscriptions. |
quote |
string |
no | none | Quote asset to pin every read to, case-insensitive. When unset, a base that trades against two quotes is double-counted. |
SOMNIA_TESTNET_PRICE_FEED is { url: "https://price-feed.dev.oracle.somnia.host/v1/graphql", quote: "USDC" }.
SOMNIA_MAINNET_PRICE_FEED is { url: "https://price-feed.prd.oracle.somnia.host/v1/graphql", quote: "USDC" }.
They index different chains; see networks and endpoints.
Transport constants
| Constant | Value | Where |
|---|---|---|
| Ordinary RPC operation | 9,000 ms cold total | 4 s handshake, 4 s response and 1 s liveness probe; zero retries. |
Heavy eth_getLogs operation |
35,000 ms cold total | 4 s handshake, 30 s response and 1 s probe. |
| Failed subscription observation | 30,000 ms lease; 64 IDs per socket | Separate from operation timeout; a late ID gets a bounded 4 s unsubscribe. Lease expiry, capacity or failed cleanup retires the socket. |
| Indexer request timeout | 30,000 ms | Every indexer read. |
| Default gas ceiling | 10,000,000 | Every SDK-signed write. Not exported. |
DEFAULT_FEES.maxFeePerGas |
60 gwei | Exported from the root entry. |
| Reconnect backoff | 500 ms, doubling to an 8 s cap | Live watches after a socket error. |
| Watch linger | about 30 s | Time a scope stays materialised after its last handle stops. |