Skip to Content
Symmio Trading-SDK — the SDK surface for builders on Arbitrum
CoreSolversOverview

Solvers

The solver APIs are off-chain REST endpoints configured per chain in createConfig. @symmio/trading-core exposes them as framework-agnostic actions plus TanStack Query option factories.

Use these APIs when you need solver metadata before building a trade flow: market lists, leverage limits, and locked percentages for a market/leverage pair.

Protocol model — intent-based, no orderbook

SYMMIO is intent-based. The current lowcap version has no orderbook. Positions are opened peer-to-peer between two roles:

  • partyA — the user. Sends an intent to open a position.
  • partyB — a solver. Watches partyA intents and takes the opposite side, becoming the direct counterparty.

Every lowcap position is one partyA ↔ one partyB agreement. There is no shared book, no matching engine, no other users on the other side. The solver is the counterparty.

Implications when building on the SDK:

  • No bid/ask depth to render. Prices come from off-chain feeds (see Price Service), not an on-chain book.
  • Trade lifecycle is intent → solver acceptance → on-chain settlement. Surface intent state, not order-fill progress.
  • partyA / partyB in ABIs, events, and notifications always mean user / solver respectively.

Markets

getMarkets

Fetch tradable markets from the chain solver’s /contract-symbols endpoint.

On Enigma, prefer getSymbols. It reads the same catalogue from the /symbols service but adds server-side filtering (name, asset, token, validity, per-side state) and pagination (limit / offset), so you fetch only the rows you need. Keep getMarkets when you want one call that also covers Rasa — /symbols is Enigma-only.

import { getMarkets } from "@symmio/trading-core"; // Omit `solverId` → the `Market` union; narrow on `kind` for solver-specific fields. const markets = await getMarkets(config, {}); const btc = markets.find((market) => market.name === "BTCUSDT"); // Pass a literal solver kind → the exact per-solver market type. const rasa = await getMarkets(config, { solverId: "rasa" }); // RasaMarket[]

Parameters

NameTypeDefaultNotes
chainIdnumber?config defaultOptional chain override.
solverIdstring?default solverA literal solver kind ("enigma" / "rasa") narrows the returned market type.

Returns — Market[], the normalized SDK market shape. Market is a discriminated union EnigmaMarket | RasaMarket on kind; fields are camelCase and required (the SDK fills solver gaps), and maxLeverage is a number on every solver. Passing a literal solverId narrows the return to that kind (getMarkets(config, { solverId: "rasa" }) → RasaMarket[]); omitting it yields the union, which you narrow on kind to reach solver-specific fields.

Important market fields (all on Market):

FieldTypeNotes
kindstringDiscriminant: "enigma" or "rasa".
symbolIdnumberContract symbol id.
namestringSolver market name, often the value to pass as symbol.
symbolstringDisplay symbol.
maxLeveragenumberMaximum leverage accepted for this market.
state (Enigma)numberEnigma only — 0 disabled, 1 close only, 2 open only, 3 enabled. Narrow on kind === "enigma" to access.
hedgerFeeCloseEarlyRate (Enigma)stringEnigma only — early (peak) close-fee rate; see Solver close fee. Paired with hedgerFeeCloseEarlyThreshold / hedgerFeeCloseStandardThreshold.

getMarketsQueryOptions

Build TanStack Query options for getMarkets.

import { getMarketsQueryOptions } from "@symmio/trading-core"; import { useQuery } from "@tanstack/react-query"; const query = useQuery(getMarketsQueryOptions(config, { query: { staleTime: 30_000 } }));

Symbols (Enigma)

The /symbols service is the richer, preferred symbol catalogue on Enigma. Where getMarkets returns the whole /contract-symbols list in one shot, /symbols lets the solver do the work: filter by name, asset, token, validity, or per-side trading state, and page through the results — so a large catalogue costs one small request instead of a full download you filter yourself.

getSymbols

Fetch the tradable symbol catalogue from the Enigma solver’s /symbols endpoint. Enigma-only — calling it against a non-enigma solver throws UNSUPPORTED_BY_SOLVER.

import { getSymbols } from "@symmio/trading-core"; // First page, the solver's default of 100 rows. const symbols = await getSymbols(config, {}); // Server-side filtering + pagination: enabled "BTC" symbols, 20 per page. const page = await getSymbols(config, { search: "BTC", isValid: "true", stateLong: "enabled", limit: 20, offset: 0, });

Parameters

NameTypeDefaultNotes
chainIdnumber?config defaultOptional chain override.
solverId"enigma"?default solverMust resolve to an enigma solver.
limitnumber?100 (max 500)Page size.
offsetnumber?0Page offset for pagination.
symbolIdnumber?—Restrict to a single symbol id.
searchstring?—Case-insensitive substring match on the symbol name.
assetstring?—Exact-match filter on the base asset.
tokenAddressstring?—Exact-match filter on the collateral token address.
isValid"true" | "false" | "any"?"true"Validity filter; the solver defaults to valid-only.
stateLong"disabled" | "close_only" | "open_only" | "enabled"?—Long-side trading-state filter.
stateShort"disabled" | "close_only" | "open_only" | "enabled"?—Short-side trading-state filter.

Returns — SolverSymbol[]. Same camelCase, gap-filled shape as an EnigmaMarket, except /symbols reports trading state per side (stateLong / stateShort) instead of the single state on getMarkets.

FieldTypeNotes
symbolIdnumberContract symbol id.
namestringSolver market name (e.g. "BTCUSDT").
symbolstringDisplay ticker.
maxLeveragenumberMaximum leverage (coerced to a number).
stateLongnumberLong-side state: 0 disabled, 1 close only, 2 open only, 3 enabled.
stateShortnumberShort-side state (same encoding).
isValidbooleanWhether the symbol is currently valid.
hedgerFeeClosestringStandard (floor) close-fee rate.
hedgerFeeCloseEarlyRatestringEarly (peak) close-fee rate — see Solver close fee.
hedgerFeeCloseEarlyThresholdnumberSeconds the early rate applies flat.
hedgerFeeCloseStandardThresholdnumberSeconds after which the standard rate applies (linear decay between the two).

Solver close fee

The solver charges more to close a freshly opened position than an aged one. The close-fee rate starts at hedgerFeeCloseEarlyRate, holds flat until hedgerFeeCloseEarlyThreshold seconds, then decays linearly to the standard hedgerFeeClose by hedgerFeeCloseStandardThreshold. These are flat fields on both EnigmaMarket (from getMarkets) and SolverSymbol (from getSymbols), so either satisfies SolverCloseFeeRates — pass a market or a symbol straight in:

import { calculateSolverCloseFee, getSolverCloseFeeRate } from "@symmio/trading-core"; const market = markets.find((m) => m.kind === "enigma" && m.symbolId === id); getSolverCloseFeeRate(market, 30); // "0.0024" — still in the early window getSolverCloseFeeRate(market, 105); // "0.0015" — halfway through the decay getSolverCloseFeeRate(market, 180); // "0.0006" — standard rate // Price a real close: holding time = now − the position's createTimestamp (seconds). const holdingSeconds = Number(nowSeconds - createTimestamp); const fee = calculateSolverCloseFee(market, { notional: "1000", holdingSeconds }); // Pass holdingSeconds: 0 for the worst case (a just-opened position) — the peak price for closing now.

A market with no decay — a non-Enigma solver, or one that omits the early fields — has hedgerFeeCloseEarlyRate === hedgerFeeClose and both thresholds 0, so getSolverCloseFeeRate returns the standard rate for every holding time.

These are the low-level pieces; for a full close-fee preview (this leg plus the platform and static legs, with the market and rates resolved for you) use getInstantCloseFees.

getSymbolsQueryOptions

Build TanStack Query options for getSymbols.

import { getSymbolsQueryOptions } from "@symmio/trading-core"; import { useQuery } from "@tanstack/react-query"; const query = useQuery(getSymbolsQueryOptions(config, { search: "ETH", limit: 50 }));

Trade volume & revenue (Enigma)

Three Enigma-only reads (each throws UNSUPPORTED_BY_SOLVER off Enigma, and each ships a matching …QueryOptions / …QueryKey factory).

getSolverRevenue

One market’s revenue totals for a trailing window — symbolId is required; the current solver generation serves no protocol-wide aggregate. Splits into a hedger-fee share and a funding share whose sum is totalRevenue, all as plain dollar numbers (no decimal scaling). Full reference: Revenue.

import { getSolverRevenue } from "@symmio/trading-core"; // One market, since listing. const lifetime = await getSolverRevenue(config, { symbolId: 1 }); // The same market, trailing 24 hours. const day = await getSolverRevenue(config, { symbolId: 1, timeRange: "24h" });

timeRange is a closed set — "1h" | "24h" | "7d" | "30d" | "lifetime" — and defaults to lifetime when omitted.

getTradeVolume

Daily trade-volume rows for one market from /trade-volume/{symbolId} — one { timestamp, volume } per day, ascending. timestamp is the solver’s ISO 8601 day bucket (e.g. "2026-07-09T00:00:00Z"); volume is a decimal string. symbolId is required. Full reference: Trade volume.

import { getTradeVolume } from "@symmio/trading-core"; const volume = await getTradeVolume(config, { symbolId: 1 }); const latest = volume.at(-1); // { timestamp: "2026-07-09T00:00:00Z", volume: "448.69…" }

getRevenueRecords

Incremental revenue records from /revenue/records, cursor-paginated on the record id. Returns { records, count }.

import { getRevenueRecords } from "@symmio/trading-core"; const { records, count } = await getRevenueRecords(config, { limit: 100 }); const nextCursor = records.at(-1)?.id; // pass back as `id` for the next page

Solver info (Enigma)

getSolverInfo

The solver’s general information from the Enigma-only /info endpoint — today its static fee configuration: the configured USD amount charged per instant open and per instant close (including each partial close), before signed fee-cap adjustments. Returns an EnigmaSolverInfo — a flat camelCase mirror of the wire shape, amounts as USD decimal strings exactly as the solver reports them, undefined where the solver omits a field.

import { getSolverInfo } from "@symmio/trading-core"; const info = await getSolverInfo(config); info.staticSolverFeeOpen; // "0.5" — USD per instant open info.staticSolverFeeClose; // "0.25" — USD per instant close (incl. partial closes)

A rasa-kind solver has no /info endpoint, so the read fails before the wire with UNSUPPORTED_BY_SOLVER rather than a vendor 404. A request that goes out and fails throws SymmApiError with code FETCH_SOLVER_INFO_FAILED. Ships the matching getSolverInfoQueryOptions / getSolverInfoQueryKey factories; the React binding is useSolverInfo.

The instant fee previews consume these legs: getInstantOpenFees reports the open leg as staticSolverFeeOpen, and prepareInstantOpenParams funds it through the addMargin amount (full-balance sizing carves it off the balance before the linear solve). The static close leg is not funded at open — the contract charges it at close from the position, and getInstantCloseFees reports it as staticSolverFeeClose (see Fees at close). Pass a pre-fetched result as solverInfo to skip the fetch in either preview.

Locked Params

getLockedParams

Fetch locked params from the chain solver’s /get_locked_params/{symbol} endpoint.

The symbol parameter is the solver path value. In trade UIs, use the selected market’s name when available, with symbol as a fallback.

import { getLockedParams } from "@symmio/trading-core"; const locked = await getLockedParams(config, { symbol: "BTCUSDT", leverage: 5, }); console.log(locked.cva, locked.lf, locked.partyAmm, locked.partyBmm);

Parameters

NameTypeDefaultNotes
symbolstringrequiredSolver market path value.
leveragenumberrequiredLeverage used to calculate locked params.
chainIdnumber?config defaultOptional chain override.

Returns — SolverLockedParams.

interface SolverLockedParams { cva?: string; lf?: string; partyAmm?: string; partyBmm?: string; leverage?: string; }

getLockedParamsQueryOptions

Build TanStack Query options for getLockedParams.

import { getLockedParamsQueryOptions } from "@symmio/trading-core"; import { useQuery } from "@tanstack/react-query"; const options = getLockedParamsQueryOptions(config, { symbol: "BTCUSDT", leverage: 5, query: { staleTime: 10_000 }, }); const query = useQuery(options);

GetLockedParamsOptions requires symbol and leverage. The query factory does not make required action inputs optional and does not validate missing inputs inside queryFn.

For form UIs where values may be incomplete, gate the query outside the factory:

const ready = Boolean(symbol) && leverage > 0; const options = getLockedParamsQueryOptions(config, { symbol: symbol ?? "", leverage: leverage ?? 0, query: { enabled: ready }, });

getLockedParamsQueryKey

Build a cache key for a locked-params query.

import { getLockedParamsQueryKey } from "@symmio/trading-core"; const key = getLockedParamsQueryKey({ symbol: "BTCUSDT", leverage: 5, });

symbol and leverage are part of the key, so each market/leverage pair caches independently.

Notional Cap (available liquidity)

Per-market caps on how much notional the solver still permits — the frontline “can I open this size?” gate.

getNotionalCapBySymbolId

Cap for one market.

import { getNotionalCapBySymbolId } from "@symmio/trading-core"; const cap = await getNotionalCapBySymbolId(config, { symbolId: 1n }); // { used: bigint, cap: bigint, availability: bigint }

getNotionalCapAll

Batch — every market’s cap in one call. Cheaper than looping getNotionalCapBySymbolId.

import { getNotionalCapAll } from "@symmio/trading-core"; const caps = await getNotionalCapAll(config, {});

getOpenInterestBySymbolId

Used vs capped notional per market.

import { getOpenInterestBySymbolId } from "@symmio/trading-core"; const oi = await getOpenInterestBySymbolId(config, { symbolId: 1n });

Estimated price

getEstimatedPrice

Ask the solver what price an open or close would actually fill at — a read-only simulation of the trade (GET /estimated-price; nothing is submitted) → { estimatedPrice }. Pass the order quantity, the side, whether it’s an "open" or "close", and the slippage-adjusted request price the caller computed (calculateTradeParams().requestedOpenPrice for an open, calculateClosePrice() for a close) — not the raw mark. position_type is sent as "long" / "short".

import { getEstimatedPrice, PositionType } from "@symmio/trading-core"; const { estimatedPrice } = await getEstimatedPrice(config, { symbolId: 1, quantity: "1000", positionType: PositionType.LONG, entry: "open", price: requestPrice, });

getEstimatedPriceQueryOptions / getEstimatedPriceQueryKey ship alongside for TanStack; the React layer wraps them as useEstimatedPrice.

calculatePriceImpact

Pure helper — signed price-impact percent of an estimated fill vs a reference price: (estimated − reference) / reference × 100. Returns 0 for a zero / non-finite reference. The UI colors it by side.

import { calculatePriceImpact } from "@symmio/trading-core"; const impact = calculatePriceImpact({ estimatedPrice, referencePrice: markPrice }); // e.g. 0.42 → +0.42%

Rasa-only solver endpoints

The rasa solver (majors, on Base) exposes reads the enigma solver does not. Each is a normal getX action with a …QueryOptions / …QueryKey factory; calling one against a non-rasa solver throws UNSUPPORTED_BY_SOLVER. In React these are the Rasa Solver hooks. See Solvers & Chains for the kind model.

ActionPurpose
getSolverReadinessWhether the solver is up and accepting trades.
getSolverBalanceInfoSolver-side balance for an account.
getPartyAUpnlPartyA uPnL as computed by the solver.
getSolverOpenInterestGlobal open interest.
getSolverPriceRangeAccepted price range for a symbol.

Notification history search is not listed here: it is the unified searchNotifications, which dispatches to the rasa solver’s position-state endpoint (POST /position-state/{start}/{size}) for a rasa solver and returns the rasa result variant. See Notifications.

supportsEstimatedPrice

Pure predicate — whether the resolved solver kind supports the /estimated-price simulation. Gate the estimated-price read with it so a majors UI doesn’t call an endpoint its solver doesn’t serve.

import { supportsEstimatedPrice } from "@symmio/trading-core"; if (supportsEstimatedPrice(config, { solverId })) { // safe to call getEstimatedPrice for this solver }

getErrorMessage

Resolve a solver error code to a human message (getErrorMessageQueryOptions / …QueryKey alongside). The React wrapper is useErrorMessage.

Instant Open / Instant Close

The lowcap trading flows live on their own pages:

  • Instant Open — instantOpen, instantOpenAuto, prepareInstantOpenParams, getInstantOpens, getInstantOpenQuoteId.
  • Instant Close — instantClose, instantCloseAuto, instantCloseBulk, prepareInstantCloseParams, getInstantCloses.
  • SYMMIO Contract — on-chain reads. getOnchainContractMarkets is the contract-side analog to getMarkets.
  • Unified Quotes — PendingInstantOpen / PendingInstantClose from getInstantOpens / getInstantCloses feed reconcileQuotes.
  • Errors — solver failures surface as SymmApiError.
Last updated on