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

Subgraph hooks

React hooks over the two per-chain GraphQL subgraphs (analytics + events). See Core Subgraph for the full data model, endpoint layout, and how to add missing queries.

Read only. Subgraphs are historical indexes — writing happens on-chain, then indexed here.

Import

import { useSubgraphQuery, useBalanceHistory, useTransferHistory, useQuoteHistory, useQuoteEventsByType, useQuoteFunding, useQuotesFunding, useQuoteGroupFunding, useQuoteGroupFundingHistory, useDepositHistory, useWithdrawHistory, } from "@symmio/trading-react";

Escape hatch — useSubgraphQuery

Send any raw GraphQL query against either subgraph. Use when the SDK doesn’t ship a pre-baked hook for what you need.

const history = useSubgraphQuery({ subgraph: "analytics", query: gql` query MyCustomQuery($account: String!) { myEntities(where: { account: $account }) { id value } } `, variables: { account: subAccount }, });

Returns UseQueryResult<T, SymmioRequestError> — same shape as every SDK hook. The response is parsed as-is; consumers own the type.

If you find yourself writing the same raw query across two components, that’s the signal to open a PR adding a typed hook — see Core Subgraph.

Pre-baked history reads

useBalanceHistory

Deposit / withdraw / allocate / deallocate events over time.

const history = useBalanceHistory({ account, first: 50, skip: 0 });

useTransferHistory

Every collateral in / out for an account.

const transfers = useTransferHistory({ account, first: 50, skip: 0 });

useDepositHistory / useWithdrawHistory

Filtered projections of the balance history for the two most common UI needs.

Per-quote history

useQuoteHistory

Terminal (close / liquidation) events for a set of accounts, newest first. Keyed on subAccounts — not on a quote id — because the subgraph filters history by the quote’s owning account. Pass the parent SubAccount and, when the positions are isolated, its Virtual Accounts.

const { data } = useQuoteHistory({ subAccounts: [subAccount], first: 100 }); const rows = data?.rows ?? [];

Each row also carries the solver fees that close charged — solverFee, staticSolverFee, and totalSolverFee, all bigint | null. They are null on liquidation and ADL rows and on deployments indexed before the fee was recorded; see solver fees.

useQuoteEventsByType

Non-terminal events for one quote, filtered to the QuoteEventTypes you ask for → { rows, hasMore }. The indexed types are SettleUpnl (open-price recompute), ChargeFundingRate, and ChargeAccumulatedFundingFee; two presets ship with the SDK — PRICE_HISTORY_EVENT_TYPES (all three, the open-price timeline) and FUNDING_HISTORY_EVENT_TYPES (the two funding charges only).

import { FUNDING_HISTORY_EVENT_TYPES } from "@symmio/trading-core"; const { data } = useQuoteEventsByType({ quoteId: 42n, types: FUNDING_HISTORY_EVENT_TYPES, first: 20, });

useQuoteFunding

Settled funding totals — paid, received, netReceived — for one quote, to date. Disabled while quoteId is undefined (an off-chain row). Funding accrued since the quote’s last settlement is a separate on-chain read: useQuotePendingFunding.

const { data, isLoading } = useQuoteFunding({ quoteId: 42n }); /** `data` is `QuoteFundingData | null`: { quoteId, paid, received, netReceived }, all wei. */ const netReceived = data?.netReceived ?? 0n;

useQuotesFunding

Batch variant — settled funding for many quotes in one request, plus the running sums. Feeds a “settled funding across open positions” figure; pair it with useQuotesPendingFunding for the unsettled part, shown separately.

It takes quote-shaped rows, not bare ids: anything with an optional quoteId (a UnifiedQuote works as-is). Off-chain rows (quoteId === undefined) are skipped silently and duplicate ids are fetched — and summed — exactly once.

const { rows, netReceived, missingQuoteIds, isLoading } = useQuotesFunding({ quotes: group.quotes });

rows is aligned 1:1 with the input quotes (null for an off-chain or not-yet-indexed row). missingQuoteIds is every requested id while the query is in flight or failed, and the not-yet-indexed subset once it settles.

Group funding

Both hooks take a QuoteGroup and derive the child quote ids themselves: optimistic children (no quoteId) are dropped and duplicates collapsed, so one grouped position costs one subgraph round-trip.

Sign convention — shared with useQuoteFunding and useQuotesFunding above: netReceived = received − paid, the P&L perspective, so a positive value means the position earned funding. This matches QuoteFundingData.netReceived and the uPnL folds, and is the inverse of the cost-positive on-chain int256. A UI that colors “money in” green renders it as-is — no negation anywhere. Every surface is listed in the core sign table.

Settled to date only. These are funding charges the protocol has already applied and the analytics subgraph has indexed. Funding accrued since a quote’s last charge is not in the subgraph and is therefore not included — read it on-chain with useQuotesPendingFunding and show it as its own figure, never added into this total.

useQuoteGroupFunding

Aggregated settled funding for a whole group — the group-level counterpart of useQuoteFunding. Reads every child in one round-trip via useQuotesFunding, then folds with core’s pure aggregateGroupFunding. The result is memoized, so it is referentially stable while the group and its rows are unchanged.

function GroupedPositionFunding({ group }: { group: QuoteGroup }) { const { funding, isLoading } = useQuoteGroupFunding({ group }); /** `funding.netReceived` is a LOWER BOUND until `isComplete` is true — never render it as final before checking. */ if (isLoading || !funding.isComplete) return <FundingSkeleton />; /** Already income-positive: `netReceived > 0n` means the group EARNED funding. */ return <Money amount={funding.netReceived} label="Funding" />; }

funding is a QuoteGroupFunding: paid / received / netReceived (wei), resolvedCount, expectedCount, missingQuoteIds, and isComplete. Treat isComplete: false as “funding unknown”, not “no funding” — an all-optimistic or empty group reports false with netReceived: 0n. rows is the per-child QuoteFundingData | null, aligned 1:1 with group.quotes.

useQuoteGroupFundingHistory

The per-tick timeline behind that total: every child’s funding events as one merged, time-sorted stream. types is locked to FUNDING_HISTORY_EVENT_TYPES, so the hook takes only the group plus paging / sort.

const { data, isLoading } = useQuoteGroupFundingHistory({ group, first: 100 }); for (const row of data?.rows ?? []) { /** Rows arrive interleaved across quotes — `row.quoteId` says which leg this tick belongs to. */ const netReceived = (row.fundingReceived ?? 0n) - (row.fundingPaid ?? 0n); /** netReceived > 0n → the position earned funding on this tick. `row.rate` is the signed rate applied (wei). */ }

Returns a TanStack UseQueryResult<{ rows: QuoteEventRow[]; hasMore: boolean }, SymmioRequestError>. Paging is over the merged stream, not per quote id: pass first / skip and keep going while data.hasMore is true. Omitting first asks for the 1000-row ceiling the subgraph enforces, so an un-paged call loads the whole timeline unless the group has more ticks than one request can return. Pass orderDirection: "asc" for an oldest-first timeline. The query stays disabled while the group has no on-chain children.

CHARGE_ACCUMULATED_FUNDING_FEE rows appear only for pairs on accumulated funding, and legacy pairs produce CHARGE_FUNDING_RATE rows — see Funding › Pair states. Do not assume every row is one type.

Last updated on