import type { BinaryFillKind, BinarySide, OrderStatus } from "./store.js"; import type { BuilderFeeRecord, ProtocolFeeRecord } from "./fees.js"; import type { Market } from "./markets.js"; /** * Options for {@link SomniaMarketsClient.getFills} / {@link SomniaMarketsClient.getUserFills}. All optional. * * @category fills */ export type FillsOptions = { /** Max rows (default 50). */ limit?: number; /** Row offset for paging the tape (default 0). */ offset?: number; /** Only fills at/after this unix-seconds timestamp. */ since?: number; /** Only fills at/before this unix-seconds timestamp. */ until?: number; }; /** * Scope for the per-account fill reads * ({@link SomniaMarketsClient.getUserFills} / * {@link SomniaMarketsClient.countUserFills}): a `market` (or `markets`) and/or * `pool` predicate on top of {@link FillsOptions}. * * Prefer `market` on binary. A binary pool is recycled by successive markets, * so `pool` selects every life of that pool, while `market` selects exactly one * market. On spot/perp the market id IS the pool address, so the two agree. * * @category fills */ export type FillsScope = FillsOptions & { /** Only fills in this market (bytes32 marketId, case-insensitive). */ market?: string; /** * Only fills in ANY of these markets (bytes32 marketIds, case-insensitive) * — the batched form of `market`, for folding several markets from one read. * An empty array matches nothing. Supplying `market` as well narrows to both * (the intersection), so neither silently overrides the other. */ markets?: readonly string[]; /** Only fills on this pool address (case-insensitive). */ pool?: string; }; /** Scope and bounded continuation for {@link SomniaMarketsClient.getUserFillsPage}. */ export type GetUserFillsPageOptions = Omit & { /** Opaque cursor from the preceding page. Omit for the newest page. */ cursor?: string; }; /** One bounded historical page. This is not a coverage or snapshot guarantee. */ export type UserFillsPage = { /** Existing fill rows, newest first by numeric timestamp, block and log position. */ fills: FillRow[]; /** Continue with the same scope. Null means no further row was observed in this read. */ nextCursor: string | null; }; /** * Recent fills for a pool (either market type), newest first — a one-shot * indexer query. For a continuously-updating trade tape on a binary pool, use * the live-store reader `getLiveFills` (or the `useLiveFills` hook) instead. * * **Details** * * - `pool`: Pool address (case-insensitive). * - `opts`: Paging + `since`/`until` window ({@link FillsOptions}). */ export declare function getFills(pool: string, opts: FillsOptions | undefined, indexerUrl: string): Promise; /** * Fills a user participated in (as maker OR taker), newest first — the one-shot * indexer counterpart to the live-store `getLiveUserFills`. Optionally scoped to * one market and/or pool and/or a `since`/`until` window. * * Scope by `market` (or `markets` for several) to get those markets' fills: on * binary a pool is recycled across successive markets, so `pool` alone also * returns the fills of that pool's earlier lives. Every predicate runs at the * indexer, so the `limit` applies to the rows you asked for rather than to a * mixed set. */ export declare function getUserFills(account: string, opts: FillsScope | undefined, indexerUrl: string): Promise; /** Owner-bound historical continuation; existing offset reads remain unchanged. */ export declare const getUserFillsPage: (account: string, options: GetUserFillsPageOptions | undefined, config: { source: string; chainId: number; signal?: AbortSignal; headers?: Record; }) => Promise; /** * One fill by its id (`${blockNumber}_${logIndex}`) with both parties' order * linkage and the market it executed on — the single lookup behind a fill * detail view. Null when the id isn't indexed (yet — the indexer can lag a * just-executed fill by a beat). */ export declare function getFill(id: string, indexerUrl: string): Promise; /** * Every fill one order participated in — either side, newest first. Order ids * are never reused (monotonic low-64 counter per pool), so `(pool, orderId)` * names exactly one order forever. Rides the (pool, timestamp) composite index * down to one pool before the order-id filter. */ export declare function getOrderFills(pool: string, orderId: bigint | string, opts: { limit?: number; } | undefined, indexerUrl: string): Promise; /** * Server-side COUNT of the fills `account` participated in (maker OR taker), * optionally scoped to one market and/or pool + a `since`/`until` window — a * history-page total without fetching rows (Hasura `Fill_aggregate`, bounded fallback on * the public role). * * WITHOUT THAT HEADER THE TOTAL IS A LOWER BOUND. The fallback scan stops at * {@link IndexerRead.COUNT_FALLBACK_CAP} rows and reports the cap, and `Fill` * is the deepest counted table in production. No bounded variant of this helper * exists yet; `countMarketsBounded` on the client is the pattern to copy. */ export declare function countUserFills(account: string, opts: FillsScope | undefined, indexerUrl: string, headers?: Record): Promise; /** * One fill as the indexer recorded it (mirror of the unified `Fill` entity — * spot, perp and binary fills share it). * * @category fills */ export type FillRow = { /** Fill id (`${blockNumber}_${logIndex}`). */ id: string; /** * The market's bytes32 marketId — the STABLE identity of the market this fill * executed in. * * Group and label by this, never by `pool` alone: a binary pool is recycled * across successive markets, so fills from a pool's earlier life carry the * same pool address as the market currently on it. On SPOT/PERP the pool * address IS the market id. Pass it to * {@link SomniaMarketsClient.getMarket | client.getMarket} for the full row. */ market: string; /** * Lowercased pool address the fill executed on. A TIME-VARYING binding — see * `market` for the identity that does not move. */ pool: string; /** * Execution price, raw quote units per whole base (binary: YES-probability * scale). SPOT/PERP: the maker's limit price. */ fillPrice: string; /** Base/outcome-token quantity filled, raw units. */ quantity: string; /** Quote/collateral value = quantity × fillPrice / 10^baseDecimals (raw, floored). */ quoteQuantity: string; /** Maker (resting) wallet, lowercased; null when unknown. */ maker: string | null; /** BINARY only — the maker's YES/NO side; null on SPOT/PERP. */ makerSide: BinarySide | null; /** * Taker wallet, lowercased. Denormalized from the taker's OrderPlaced (which * fires after the fill in the same tx) — null until that bridge lands. */ taker: string | null; /** * BINARY only — the taker's YES/NO side; null on SPOT/PERP or until the * taker's OrderPlaced is bridged. */ takerSide: BinarySide | null; /** * BINARY only — how the fill settled (direct trade vs mint/burn of a pair); * null on SPOT/PERP or until the taker side is known. */ kind: BinaryFillKind | null; /** * True when the taker bought the base/YES (the maker was the ask); null until * the taker side is known. */ takerIsBid: boolean | null; /** * The taker's ORDER (owner + side), when the indexer has it. * * Prefer `takerOrder.side` over {@link FillRow.takerSide} on binary: the * latter is a denormalized copy the taker bridge backfills, so it lags and * can be null on a row that already names its taker. */ takerOrder: { owner: string; side: BinarySide | null; } | null; /** uint128 id of the resting (maker) order, decimal string. */ makerOrderId: string; /** uint128 id of the aggressing (taker) order, decimal string. */ takerOrderId: string; /** Timestamp (unix seconds) of the fill. */ timestamp: string; /** Tx hash the fill landed in. */ txHash: string; }; /** * A fill with its order linkage — {@link FillRow} plus the two order ids and * the post-fill remainders. What {@link SomniaMarketsClient.getOrderFills} returns. */ export type OrderFillRow = FillRow & { /** Taker order's unfilled remainder AFTER this fill, raw units. */ takerRemainingQuantity: string; /** Maker order's unfilled remainder AFTER this fill, raw units. */ makerRemainingQuantity: string; /** Block the fill landed in (decimal string). */ blockNumber: string; /** Log index within the block (with blockNumber: the fill's id). */ logIndex: number; }; /** * The market a fill/order belongs to, as detail reads embed it — enough to * label and scale the row (symbols + decimals) and route to the market's page, * without dragging in the full per-kind {@link Market} union. */ export type MarketRef = { /** Market entity id (pool address for SPOT/PERP; marketId bytes32 for BINARY). */ id: string; marketType: "SPOT" | "PERP" | "BINARY"; /** Lowercased pool address serving the market. */ poolAddress: string; /** BinaryMarket contract address; null on SPOT/PERP. */ marketAddress: string | null; baseSymbol: string | null; quoteSymbol: string | null; baseDecimals: number; quoteDecimals: number; /** Underlying asset label (BINARY); null on SPOT/PERP. */ asset: string | null; /** The market's question text (BINARY); null on SPOT/PERP. */ question: string | null; }; /** * {@link OrderFillRow} plus the market it executed on — one fill, fully framed. * * The embed is `marketRef`, not `market`, because the name is already taken: * {@link FillRow.market} is the bytes32 marketId STRING (aliased from * `market_id` in `FillQueryFields`, load-bearing for binary-PnL market * scoping). Two fields cannot share it — GraphQL refuses to select the * `market` relationship alongside the `market: market_id` alias, and the TS * intersection `string & MarketRef` is uninhabitable. */ export type FillDetail = OrderFillRow & { marketRef: MarketRef; }; /** * One side's order on a fill — the resting order, or the one that crossed it. * * A narrower shape than {@link OrderRow}: this describes an order in the context * of a fill whose market is already known, so it carries no market labelling. * Amounts are raw units. */ export type FillOrder = { /** Order id (`${pool}_${orderId}`). */ id: string; /** uint128 OrderId as a decimal string. */ orderId: string; /** Owner wallet, lowercased. */ owner: string; /** * True = bid (buy). Set on every market kind, unlike `side`, which the indexer * fills in only for binary. */ isBid: boolean; /** BINARY only — the YES/NO side; null on spot and perp. */ side: BinarySide | null; /** Limit price, raw quote units per whole base. */ price: string; /** Original size, raw base/outcome units. */ fullQuantity: string; /** Cumulative filled size, raw base/outcome units. */ filledQuantity: string; /** Unfilled remainder, raw base/outcome units. */ quantityRemaining: string; /** Reconciled lifecycle status (Open/Filled/Cancelled/Expired/Closed). */ status: OrderStatus; /** Whether the order ever rested on the book (an `OrderRested` fired). */ rested: boolean; /** * WHY the PROTOCOL cancelled the order, when it was not the owner. Null for an * owner cancel and for an order that was never cancelled — so a `Cancelled` * status with a null reason means the owner did it. */ cancelReason: string | null; /** Timestamp (unix seconds) the order was placed. */ placedAtTimestamp: string; /** Transaction the order was PLACED in — usually not the fill's transaction. */ placedTxHash: string; }; /** * Everything the indexer knows about ONE fill: the trade itself, the market it * executed in, both sides' orders, the fees it paid, and the other fills its * transaction produced. * * The shape of a trade detail view. Each piece may be absent on its own terms — * see the field docs — and absence is normal rather than an error. * * Distinct from {@link FillDetail}, which is the one-query lookup behind * {@link SomniaMarketsClient.getFill}: that names the fill and its market, this * adds the surrounding CONTEXT — both orders resolved, the fees, the rest of the * transaction — at the cost of a second round-trip. A caller that only needs to * render the trade wants `getFill`. */ export type TradeContext = { /** The fill, with its block position and post-fill remainders. */ fill: OrderFillRow; /** * The market the fill executed in; null only when the indexer has no market * row for it (an unregistered pool). */ market: Market | null; /** * The resting order that was filled; null until the indexer has that order's * row. */ makerOrder: FillOrder | null; /** The aggressing order that crossed the book; null until its row is indexed. */ takerOrder: FillOrder | null; /** * The OTHER fills of the same transaction, newest first — the rest of a * taker's sweep. Empty when this fill was the whole trade. Excludes this fill. */ siblings: OrderFillRow[]; /** * Protocol fees charged in the same transaction. BINARY only, and empty when * no fee was skimmed. * * Transaction-scoped, not fill-scoped: a fee record names the ORDER it was * charged on, not the fill, so a multi-fill sweep cannot be split into * per-fill fees. Match `orderId` against the fill's `makerOrder`/`takerOrder` * to attribute what can be attributed. */ protocolFees: ProtocolFeeRecord[]; /** Builder fees charged in the same transaction, on the same terms as {@link TradeContext.protocolFees}. */ builderFees: BuilderFeeRecord[]; }; /** * One fill in full, by id — the trade, its market, both orders, its fees, and * the rest of its transaction. * * This is the read behind a trade detail view. Use it when a caller has picked * ONE trade out of a tape or activity feed and wants everything about it; * {@link getFills} and `getMarketActivity` are the list reads that produce the id. * * Returns `null` when no fill has this id — a mistyped or stale link, not a * failure. A failed read throws (SDK-IO-002). * * Two round-trips: the fill (with its market and both orders) has to resolve * before its transaction's siblings and fees can be selected, because those are * anchored on the fill's timestamp so the indexer can serve them from an index. * * @param id - Fill id, `${blockNumber}_${logIndex}` (as `FillRow.id` carries it). * @throws {@link IndexerError} when the indexer read fails. * * ```ts * const detail = await client.getTradeContext("441083911_5"); * if (detail) { * console.log(detail.fill.fillPrice, detail.makerOrder?.owner, detail.siblings.length); * } * ``` */ export declare function getTradeContext(id: string, indexerUrl: string): Promise; /** * The selection behind {@link MarketRef}, declared ONCE. Both detail reads land * their row through `narrowIndexerInvariant`, an unchecked cast — so a field * added to `MarketRef` and to only one of two copied selections would surface * as `undefined` at runtime with no type error. One fragment removes that. */ export declare const MarketRefFields: import("./gql/graphql.js").TypedDocumentString;