Appearance
Proto schema
The canonical schema for flint.spot.v1. Synced from the flint-protos submodule; regenerate SDKs and docs after bumping that submodule.
- Download bundled
api.proto— every service inflint.spot.v1. - Download
openapi.yaml— OpenAPI 3.1 schema for the gRPC-Web service URLs.
proto
// Code generated by `make bundle-proto`; DO NOT EDIT.
// Services are grouped first so the RPC surface is easy to scan.
syntax = "proto3";
package flint.spot.v1;
// -- Services --------------------------------------------------------
// -- AuthService (auth/service.proto) --------------------------------------------------------
// Issues session tokens. Keypair-auth tokens from Authenticate last 6 hours;
// passwordless organization tokens from VerifyLoginCode last 7 days.
// All RPCs are unauthenticated.
service AuthService {
// Get a nonce to sign. Idempotent per pubkey within the nonce TTL;
// calling twice returns distinct nonces but only the most recent is
// accepted by Authenticate.
rpc Challenge(ChallengeRequest) returns (ChallengeResponse);
// Exchange a signed nonce for a 6-hour keypair session token. UNAUTHENTICATED if the
// pubkey has no outstanding nonce, the nonce has expired, the signature
// is invalid, or the pubkey is not registered as a quoting authority.
rpc Authenticate(AuthenticateRequest) returns (AuthenticateResponse);
// Invalidate a token before its natural expiration.
rpc Revoke(RevokeRequest) returns (RevokeResponse);
// Send a 6-digit passwordless login code to an organization user's email.
// Requests are limited to one per email per minute.
rpc RequestLoginCode(RequestLoginCodeRequest) returns (RequestLoginCodeResponse);
// Exchange a passwordless login code for a 7-day bearer session token.
rpc VerifyLoginCode(VerifyLoginCodeRequest) returns (VerifyLoginCodeResponse);
}
// -- MarketDataService (market_data/service.proto) --------------------------------------------------------
// Public market data.
service MarketDataService {
// Subscribe to a multiplexed stream of book events (L1/L2/L3) and
// StatusEvents. The pair set is fixed at stream start; to change
// subscriptions open a new stream.
rpc Subscribe(SubscribeRequest) returns (stream MarketDataEvent);
// Subscribe to fills on a separate stream. Optional per-pair filter;
// empty = fills from every pair.
rpc SubscribeFills(SubscribeFillsRequest) returns (stream FillEvent);
// Subscribe to trades executed through external DEX aggregators
// (Jupiter, OKX, DFlow, Titan, ...) for a single requested pair. The
// server taps a shared upstream transaction feed only while at least one
// subscriber is connected; with no subscribers, nothing is captured.
rpc SubscribeExternalFills(SubscribeExternalFillsRequest)
returns (stream ExternalFillEvent);
// Subscribe to per-venue bid/ask for a single requested pair, derived by
// polling each external aggregator's quote API at a fixed notional every
// N ms. Polling for a pair runs only while at least one subscriber is
// connected. Only pairs quoted in the global (USD) numeraire are supported.
rpc SubscribeExternalQuotes(SubscribeExternalQuotesRequest)
returns (stream ExternalVenueQuoteEvent);
// One-shot book snapshot. Convenient for cold-start reconciliation
// without opening a streaming RPC.
rpc GetBook(GetBookRequest) returns (GetBookResponse);
// Return the catalog of discovered pairs + their spot metadata.
rpc ListPairs(ListPairsRequest) returns (ListPairsResponse);
// Stream a consolidated snapshot of every registered market
rpc SubscribeMarketSnapshots(SubscribeMarketSnapshotsRequest)
returns (stream MarketsSnapshotEvent);
}
// -- MakerService (maker/service.proto) --------------------------------------------------------
// Per-maker queries + streaming. Every RPC requires organization-oriented
// auth: either a session token via `authorization: Bearer <token>` metadata
// whose maker_id exists in the organizations table (see AuthService), or an
// `x-api-key` organization API key. The resolved maker_id scopes every response
// except GetDecodedTransaction, which authenticates the caller but returns the
// whole (multi-party) transaction rather than one maker's slice of it.
service MakerService {
// Stream balance updates as the on-chain accounts change.
rpc SubscribeBalance(SubscribeBalanceRequest) returns (stream MakerBalanceEvent);
// One-shot snapshot of current balances - useful on cold start before
// opening the stream.
rpc GetBalance(GetBalanceRequest) returns (GetBalanceResponse);
// Stream a consolidated cross-market snapshot enriched with the
// authenticated maker's own best bid / ask + depth level per pair.
rpc SubscribeMarketSnapshots(SubscribeMakerMarketSnapshotsRequest)
returns (stream MakerMarketsSnapshotEvent);
// 1h / 24h / 30d volume + fill count broken down by pair, scoped to
// the authenticated maker.
rpc GetVolumeBreakdown(GetMakerVolumeBreakdownRequest)
returns (GetMakerVolumeBreakdownResponse);
// Time-bucketed volume series scoped to the authenticated maker.
rpc GetVolumeSeries(GetMakerVolumeSeriesRequest)
returns (GetMakerVolumeSeriesResponse);
// Time-bucketed historical NAV (USD net asset value) scoped to the
// authenticated maker.
rpc GetNavHistory(GetNavHistoryRequest)
returns (GetNavHistoryResponse);
// Stream every fill where the authenticated maker provided the
// liquidity. The taker (counterparty) is the transaction signer; this
// stream does not include fills where the maker was the taker.
rpc SubscribeFills(SubscribeMakerFillsRequest) returns (stream MakerFillEvent);
// Historical fills where the authenticated maker provided liquidity.
// Same row shape as SubscribeFills, ordered per the request's `order_by`.
// Capped at 1000 rows - use GetMarkoutSummary for anything spanning the
// whole window rather than summing these.
rpc GetFills(GetMakerFillsRequest) returns (GetMakerFillsResponse);
// Markout over the whole window: totals, 24h buckets, per-market rows and
// the bps distribution. Aggregated in the warehouse, so no row cap.
rpc GetMarkoutSummary(GetMakerMarkoutSummaryRequest)
returns (GetMakerMarkoutSummaryResponse);
// Rolling 1s / 1m / 5m activity rates scoped to the authenticated maker:
// fill rate, TX submission outcomes, plus the venue-wide oracle update
// rate for cross-reference.
rpc GetStats(GetMakerStatsRequest) returns (GetMakerStatsResponse);
// Historical on-chain account actions by the authenticated maker:
// collateral deposits / withdrawals, maker-account creation, and market
// joins. Trading fills are excluded - use GetFills. Ordered newest-first.
rpc GetActivity(GetMakerActivityRequest) returns (GetMakerActivityResponse);
// -- Transaction explorer ----------------------------------------------
// Signature-first read of a single DECODED transaction, backing the in-house
// explorer. Returns only transactions the indexer decoded into the `events`
// table (Flint-program txs) - NOT arbitrary Solana transactions; unseen
// signatures return NOT_FOUND and the client falls back to an external
// explorer. Authenticated like the rest of the service, but NOT maker-scoped:
// it returns every decoded event in the transaction (a tx is multi-party),
// not just the caller's.
rpc GetDecodedTransaction(GetDecodedTransactionRequest)
returns (GetDecodedTransactionResponse);
// -- Quoting activity --------------------------------------------------
// On-chain quote-strategy updates (UpdateQuotingParams) by the authenticated
// maker - the high-frequency complement to GetActivity. Strategy detail
// (orderlist levels / oracle offsets / linear params) rides as JSON in
// QuoteUpdate.payload_json; the strategy enum says how to read it. Scoped to
// the maker, so a competitor can't read another maker's strategy params.
// Live stream of the maker's quote updates. Optional market / quoter filters
// narrow within the maker (both empty = all of the maker's quoting).
rpc SubscribeQuotes(SubscribeQuotesRequest) returns (stream QuoteUpdate);
// Recent quote updates by the maker over a time window, newest first.
rpc ListRecentQuotes(ListRecentQuotesRequest) returns (ListRecentQuotesResponse);
// One of the maker's quote updates in full, by on-chain identity
// (signature + instruction index + leg index). NOT_FOUND if there is no such
// quote for this maker.
rpc GetQuote(GetQuoteRequest) returns (GetQuoteResponse);
// -- Quoter liveness ---------------------------------------------------
// Connection telemetry for the maker's quoting fleet (formerly
// MakerAdminService), read from the quoter_sessions tape.
// One row per of the maker's quoting authorities (quoting keypairs) with
// currently-open sessions, aggregated across server instances, optionally
// restricted to one authority. Beyond connection state each row carries the
// fee-payer SOL balance, recent round-trip percentiles, and a short
// bucketed history of latency / quote rate / transaction outcomes - the
// whole quoter-detail page in one call.
rpc ListQuotingAuthorities(ListQuotingAuthoritiesRequest)
returns (ListQuotingAuthoritiesResponse);
// The maker's currently-open quoter sessions, optionally restricted to one
// quoting authority.
rpc ListQuoterSessions(ListQuoterSessionsRequest)
returns (ListQuoterSessionsResponse);
// -- SDK access --------------------------------------------------------
// Read-only Cargo-registry credentials for pulling the private Rust SDK
// crate. The first call mints a named token for the maker under a dedicated
// read-only registry account; later calls return the same credentials.
// Demo sessions are rejected with PERMISSION_DENIED.
rpc GetSdkCredentials(GetSdkCredentialsRequest)
returns (GetSdkCredentialsResponse);
}
// -- StatsService (stats/service.proto) --------------------------------------------------------
// Public (unauthenticated) aggregate stats about the exchange.
service StatsService {
rpc GetSummary(GetSummaryRequest) returns (GetSummaryResponse);
// 1h / 24h / 30d volume + fill count broken down by pair.
rpc GetVolumeBreakdown(GetVolumeBreakdownRequest) returns (GetVolumeBreakdownResponse);
rpc GetVolumeSeries(GetVolumeSeriesRequest) returns (GetVolumeSeriesResponse);
rpc GetAssetTvls(GetAssetTvlsRequest) returns (GetAssetTvlsResponse);
}
// -- NodeService (node/service.proto) --------------------------------------------------------
// Public (unauthenticated) identity of the flint-server node handling the
// connection: build, deploy target, and how close it sits to leaders.
service NodeService {
rpc GetNodeInfo(GetNodeInfoRequest) returns (GetNodeInfoResponse);
// Every flint-server endpoint the deployment publishes, so a client can
// discover the full set from any one node and pick the closest.
rpc ListNodes(ListNodesRequest) returns (ListNodesResponse);
}
// -- TxService (tx/service.proto) --------------------------------------------------------
// Transaction support: chain-tip feeds (blockhash, slot), transaction
// submission, and per-tx lifecycle status.
//
// Every RPC on this service requires a keypair-auth session token via
// `authorization: Bearer <token>` metadata. The token's maker_id is used
// for rate limiting on the public streams and to scope which signatures
// show up on the status stream.
service TxService {
// Forward a transaction to upstream submission paths. Returns
// the parsed signature immediately; landing/confirmation are reported
// asynchronously on `SubscribeTxStatus`.
rpc SubmitTx(SubmitTxRequest) returns (SubmitTxResponse);
rpc SubscribeBlockhash(SubscribeBlockhashRequest) returns (stream BlockhashEvent);
rpc SubscribeSlots(SubscribeSlotsRequest) returns (stream SlotEvent);
rpc SubscribeTxStatus(SubscribeTxStatusRequest) returns (stream TxStatusEvent);
// Stream the upcoming leader schedule with each leader's TPU socket, so a
// client can send directly at the validator producing the block. On connect
// the server replays its whole lookahead window before pushing new entries.
rpc SubscribeLeaders(SubscribeLeadersRequest) returns (stream LeaderEvent);
}
// -- SpreadProtectService (spread_protect/service.proto) --------------------------------------------------------
// SpreadProtect: an advisory signal for Solana network-topology risk -
// conditions that make it harder to reprice in time and easier to get picked
// off. v0 covers bad leaders only. It is an optimization layer: the on-chain
// price band and decay are the safety floor, so if SpreadProtect is down the
// maker falls back to those and is fine.
//
// The server owns the policy (a manually-maintained bad-leader list, the
// lookahead, and the widen size), turns the leader schedule plus the list into
// widen windows, and pushes them to the maker ahead of time. The signal is
// advisory: the maker may apply it, scale it, or ignore it. Nothing is
// auto-applied at the venue level.
//
// Requires a keypair-auth session token via `authorization: Bearer <token>`.
service SpreadProtectService {
// Stream SpreadProtect state resets and upcoming widen windows. On connect
// the server sends a reset event followed by any currently relevant windows,
// so a fresh or restarted client is never blind until the next scheduled push.
rpc SubscribeSpreadProtect(SubscribeSpreadProtectRequest) returns (stream SpreadProtectEvent);
}
// -- HistoricalService (historical/service.proto) --------------------------------------------------------
// Historical (ClickHouse-backed) queries. Every RPC requires a session
// token via `authorization: Bearer <token>` metadata.
//
// Hard constraints enforced by the server:
// - Maximum time window: 30 days.
// - `GetFills.limit` capped at 1000.
// - `GetCandles` returns at most 10_000 rows per call.
//
// If the backing ClickHouse is not configured, every RPC returns
// FAILED_PRECONDITION.
service HistoricalService {
rpc GetFills(GetFillsRequest) returns (GetFillsResponse);
rpc GetCandles(GetCandlesRequest) returns (GetCandlesResponse);
}
// -- EscalationService (escalation/service.proto) --------------------------------------------------------
// Out-of-band escalation path for makers to reach the FLINT on-call team.
// Requires the same organization-oriented auth as MakerService: either a
// session token via `authorization: Bearer <token>` metadata whose maker_id
// exists in the organizations table (see AuthService), or an `x-api-key`
// organization API key. The resolved maker_id is attached to every page so
// the team knows who is escalating.
service EscalationService {
// Page the on-call team with a free-text message. Returns once the page
// has been accepted and dispatched to the team's alerting channel; the
// response confirms receipt only, not that anyone has acted on it.
// UNAUTHENTICATED if the caller is not a recognized maker;
// INVALID_ARGUMENT if the message is empty.
rpc PageTheTeam(PageTheTeamRequest) returns (PageTheTeamResponse);
}
// -- Messages, events, and shared types ------------------------------
// -- Shared types (common.proto) --------------------------------------------------------
// Shared primitives used across every service: ids, solana-level crypto
// primitives, decimal wire format, enums, book level structs, and metadata.
// Every message in this package has `package flint.spot.v1;` so that the
// generated Rust module stays flat (`proto::<Type>`) regardless of which .proto
// a type was declared in.
// -- Ids -------------------------------------------------------------
message SpotId {
uint64 id = 1;
}
message MakerId {
uint64 id = 1;
}
message Pair {
reserved 1, 2;
optional uint64 base_id = 3;
string base = 4;
optional uint64 quote_id = 5;
string quote = 6;
string label = 7;
}
// -- Solana-level primitives -----------------------------------------
//
// Pubkeys and transaction signatures are carried as base58 strings on every
// message in this API.
message Blockhash {
bytes hash = 1;
}
// -- Decimal ---------------------------------------------------------
// Human-readable decimal string (e.g. "155.14"). The client must parse
// with a decimal library that matches rust_decimal semantics.
message Decimal {
string value = 1;
}
// -- Timestamp -------------------------------------------------------
// Unix microseconds since epoch. The single canonical timestamp unit
// across the entire API - events, responses, range queries. Field value
// `0` means "unspecified" (used on optional bounds in range queries and
// on absent event timestamps).
message Timestamp {
uint64 micros = 1;
}
// -- Enums -----------------------------------------------------------
enum FeedLevel {
FEED_LEVEL_UNSPECIFIED = 0;
FEED_LEVEL_L1 = 1;
FEED_LEVEL_L2 = 2;
FEED_LEVEL_L3 = 3;
}
enum Side {
SIDE_UNSPECIFIED = 0;
SIDE_BUY = 1;
SIDE_SELL = 2;
}
enum HealthState {
HEALTH_STATE_UNSPECIFIED = 0;
HEALTH_STATE_HEALTHY = 1;
HEALTH_STATE_DEGRADED = 2;
HEALTH_STATE_HALTED = 3;
}
// -- Book levels -----------------------------------------------------
message L2Level {
Decimal price = 1;
Decimal size = 2;
}
message L3Entry {
MakerId maker_id = 1;
Decimal price = 2;
Decimal size = 3;
}
// -- Subscription descriptor -----------------------------------------
message PairSubscription {
Pair pair = 1;
FeedLevel level = 2;
// When true, the server emits a full snapshot on every book update and
// never sends deltas. When false (default), one snapshot on subscribe
// followed by incremental deltas.
bool snapshot_only = 3;
}
// -- Per-event metadata ----------------------------------------------
message PairMetadata {
uint64 slot = 1;
Timestamp ts = 2;
uint64 update_id = 3;
}
// -- Reference / catalog ---------------------------------------------
message SpotMetadata {
SpotId id = 1;
string name = 2;
// Base58 mint address.
string mint = 3;
// Base58 program id.
string program_id = 4;
uint32 decimals = 5;
uint32 atoms_per_lot = 6;
// Base58 SPL Token / Token-2022 program that owns the mint.
string token_program = 7;
// Token logo URL. Empty when unknown - sourced from Jupiter's verified
// token set, so it needs the pricing store enabled.
string icon = 8;
}
// -- Geo -------------------------------------------------------------
// A geographic coordinate. Optional everywhere it appears: the source may be
// unavailable (no GeoIP database loaded, a private/unresolvable IP, or an
// unknown server region), in which case the field is simply absent.
message LatLng {
double lat = 1;
double lng = 2;
}
// -- Health / status (cross-cutting) ---------------------------------
message StatusEvent {
HealthState state = 1;
// Absent = global scope, present = per-pair.
Pair pair = 2;
string reason = 3;
Timestamp ts = 4;
}
// -- Auth messages (auth/messages.proto) --------------------------------------------------------
// -- AuthService messages --------------------------------------------
//
// Session-token flow:
// 1. Client calls `AuthService.Challenge(pubkey)` -> server returns a
// single-use nonce bound to that pubkey with a short TTL.
// 2. Client signs AUTH_DOMAIN_PREFIX || nonce with the pubkey's keypair.
// 3. Client calls `AuthService.Authenticate(pubkey, signature)` -> server
// verifies the signature covers the outstanding nonce, maps pubkey to
// maker_id via MakerRegistry, and returns a bearer `session_token`.
// 4. Client attaches `authorization: Bearer <session_token>` metadata on
// TxService or MakerService. Passwordless sessions are accepted by
// MakerService only, not TxService.
message ChallengeRequest {
// Base58 pubkey (quoting authority) that will sign the challenge.
string pubkey = 1;
}
message ChallengeResponse {
// Single-use random nonce. Client signs AUTH_DOMAIN_PREFIX || nonce.
bytes nonce = 1;
// Absolute expiration. Server rejects Authenticate after this moment.
Timestamp expires_at = 2;
}
message AuthenticateRequest {
// Base58 pubkey.
string pubkey = 1;
// Base58 signature over AUTH_DOMAIN_PREFIX || nonce from the outstanding Challenge.
string signature = 2;
// Maker to authenticate as. Only needed when `pubkey` is a quoting
// authority for more than one maker (a shared quoting key); the server then
// scopes the session to exactly this maker. When omitted, the server
// resolves the maker the pubkey maps to; if the pubkey is a quoting
// authority for more than one maker it defaults to the lowest maker id and
// logs a warning, so set this for a deterministic maker.
MakerId maker_id = 3;
}
message AuthenticateResponse {
// Opaque bearer token. Clients pass this back as
// `authorization: Bearer <session_token>` metadata on all authenticated RPCs.
string session_token = 1;
// Resolved maker identity the token speaks for.
MakerId maker_id = 2;
// Absolute session expiration. Clients should re-auth before this
// moment; server will start returning UNAUTHENTICATED once it passes.
Timestamp expires_at = 3;
}
message RevokeRequest {
string session_token = 1;
}
message RevokeResponse {}
message RequestLoginCodeRequest {
string email = 1;
}
message RequestLoginCodeResponse {}
message VerifyLoginCodeRequest {
string email = 1;
string code = 2;
}
message VerifyLoginCodeResponse {
// Opaque bearer token. Clients pass this back as
// `authorization: Bearer <session_token>` metadata on MarketDataService.
string session_token = 1;
// Resolved maker identity associated with the user's organization.
MakerId maker_id = 2;
// Absolute session expiration.
Timestamp expires_at = 3;
string organization_name = 4;
}
// -- Market data events (market_data/events.proto) --------------------------------------------------------
// -- Book events -----------------------------------------------------
message L1Event {
Pair pair = 1;
PairMetadata metadata = 2;
L2Level bid = 3;
L2Level ask = 4;
}
message L2SnapshotEvent {
Pair pair = 1;
PairMetadata metadata = 2;
repeated L2Level bids = 3;
repeated L2Level asks = 4;
}
message L2UpdateEvent {
Pair pair = 1;
PairMetadata metadata = 2;
repeated L2Level bids = 3;
repeated L2Level asks = 4;
}
message L3SnapshotEvent {
Pair pair = 1;
PairMetadata metadata = 2;
repeated L3Entry bids = 3;
repeated L3Entry asks = 4;
}
message L3UpdateEvent {
Pair pair = 1;
PairMetadata metadata = 2;
repeated L3Entry bids = 3;
repeated L3Entry asks = 4;
}
// -- Cross-market snapshot -------------------------------------------
// `slot` is per-market: different entries may carry different slots when
// only a subset of markets updated in the most recent envelope.
message MarketSnapshot {
Pair pair = 1;
uint64 slot = 2;
L2Level best_bid = 3;
L2Level best_ask = 4;
repeated L2Level bid_depth_levels = 5;
repeated L2Level ask_depth_levels = 6;
Decimal last_price = 7;
}
message MarketsSnapshotEvent {
uint64 slot = 1;
Timestamp ts = 2;
repeated MarketSnapshot markets = 3;
}
// -- Fill events -----------------------------------------------------
message FillEvent {
Pair pair = 1;
Side side = 2;
Decimal price = 3;
Decimal size = 4;
uint64 slot = 5;
Timestamp ts = 6;
// Base58 signature of the transaction the fill was settled in.
string signature = 7;
}
// -- External aggregator fills ---------------------------------------
// A trade observed on an external Solana DEX aggregator (Jupiter, OKX,
// DFlow, Titan, ...), normalized onto a Flint pair. `price` is quote per
// base and `size` is in base units, both derived from the swap's token
// balance deltas; `side` is the taker's direction relative to the base.
message ExternalFillEvent {
Pair pair = 1;
Side side = 2;
Decimal price = 3;
Decimal size = 4;
uint64 slot = 5;
Timestamp ts = 6;
// Base58 signature of the transaction the swap settled in.
string signature = 7;
// Aggregator the swap routed through ("jupiter", "okx", "dflow", "titan").
string source = 8;
}
// -- External venue quotes -------------------------------------------
// A venue's bid/ask for a pair at a fixed notional, obtained by polling the
// venue's quote API. Both sides are quoted at the same base size (the amount
// of base received for the ask-side notional), so `bid.size == ask.size`;
// under spread/fees the realized bid notional is <= the ask notional. `price`
// is quote per base. The quote side is the global (USD) numeraire.
message ExternalVenueQuoteEvent {
Pair pair = 1;
string venue = 2;
// SELL base -> quote (price a taker receives selling base).
L2Level bid = 3;
// BUY base with quote (price a taker pays buying base).
L2Level ask = 4;
// Server receipt time of the poll that produced this quote.
Timestamp received_ts = 5;
// Underlying AMM/DEX labels each side's order routes through, in route
// order - e.g. Jupiter's `routePlan[].swapInfo.label`. The buy and sell
// legs are quoted separately and can route differently. Empty if the venue
// doesn't report a route.
repeated string ask_route = 6;
repeated string bid_route = 7;
}
// -- Multiplexed stream envelope -------------------------------------
//
// MarketDataService.Subscribe returns a single stream carrying every
// per-pair book event plus cross-cutting StatusEvents. Fills are not
// carried here - use `MarketDataService.SubscribeFills`. Clients
// discriminate via `kind`.
message MarketDataEvent {
oneof kind {
L1Event l1 = 1;
L2SnapshotEvent l2_snapshot = 2;
L2UpdateEvent l2_update = 3;
L3SnapshotEvent l3_snapshot = 4;
L3UpdateEvent l3_update = 5;
StatusEvent status = 6;
}
}
// -- Market data messages (market_data/messages.proto) --------------------------------------------------------
// -- MarketDataService request / response ----------------------------
message SubscribeRequest {
// Pairs to subscribe to with their desired feed level.
repeated PairSubscription pairs = 1;
}
// Separate stream so book subscribers don't have to pay to deserialize
// fills and vice versa.
message SubscribeFillsRequest {
// Per-pair filter. Empty = fills from every pair.
repeated Pair pairs = 1;
}
// External aggregator fills are streamed per-pair: the server captures a
// single shared upstream transaction feed across all aggregators and fans
// out only the trades matching each subscriber's requested pair.
message SubscribeExternalFillsRequest {
// Required. The pair to stream external fills for.
Pair pair = 1;
}
// Per-venue bid/ask poll feed. The server polls each configured venue's quote
// API for this pair while at least one subscriber is connected. The pair's
// quote side must be the global (USD) numeraire.
message SubscribeExternalQuotesRequest {
// Required. The pair to stream external venue quotes for.
Pair pair = 1;
}
message GetBookRequest {
Pair pair = 1;
FeedLevel level = 2;
}
message GetBookResponse {
oneof snapshot {
L2SnapshotEvent l2 = 1;
L3SnapshotEvent l3 = 2;
L1Event l1 = 3;
}
}
message ListPairsRequest {}
message SubscribeMarketSnapshotsRequest {}
message ListedPair {
Pair pair = 1;
SpotMetadata base = 2;
// Last landed fill price for this pair. Absent until the server observes a fill.
Decimal last_price = 3;
SpotMetadata quote = 4;
// Taker fee rate for this pair, in hundredths of a basis point:
// 1_000_000 == 100%, so 100 == 1bp == 0.01%.
uint32 fee_hbps = 5;
}
message ListPairsResponse {
repeated ListedPair pairs = 1;
}
// -- Maker events (maker/events.proto) --------------------------------------------------------
// Streaming form of a per-maker balance observation. Emitted whenever the
// underlying on-chain account updates. Always scoped to the authenticated
// maker - servers filter by the token's maker_id before fan-out, so a
// client never sees another maker's balances.
message MakerBalanceEvent {
MakerId maker_id = 1;
SpotId spot_id = 2;
// Token units (e.g. "1.5" SOL); server scales by SpotMetadata.decimals.
Decimal balance = 3;
uint64 slot = 4;
Timestamp ts = 5;
Decimal notional = 6;
Decimal soft_max_balance = 7;
}
// -- Per-maker market snapshot ---------------------------------------
// The authenticated maker's resting quote on one side of a pair plus
// its 0-indexed L2 depth level (0 = top of book). Aggregates across the
// maker's individual orders at their best price for that side.
message MakerQuote {
Decimal price = 1;
Decimal size = 2;
uint32 level = 3;
}
// Per-pair view that augments `MarketSnapshot` with the authenticated
// maker's own best bid / best ask + depth level.
message MakerMarketSnapshot {
Pair pair = 1;
uint64 slot = 2;
L2Level best_bid = 3;
L2Level best_ask = 4;
repeated L2Level bid_depth_levels = 5;
repeated L2Level ask_depth_levels = 6;
Decimal last_price = 7;
// Absent when the maker has no order on this side of this pair.
MakerQuote maker_bid = 8;
MakerQuote maker_ask = 9;
}
message MakerMarketsSnapshotEvent {
uint64 slot = 1;
Timestamp ts = 2;
repeated MakerMarketSnapshot markets = 3;
}
// -- Per-maker fill --------------------------------------------------
// Fill where the authenticated maker provided the liquidity. The `side`
// here is the maker's side (BUY = maker bought, SELL = maker sold) -
// the inverse of `FillEvent.side` on the public market-data stream,
// which carries the taker's side.
message MakerFillEvent {
Pair pair = 1;
MakerId maker_id = 2;
Side side = 3;
Decimal price = 4;
Decimal size = 5;
uint64 slot = 6;
Timestamp ts = 7;
// Base58 signature of the transaction the fill was settled in.
string signature = 8;
// Quote-denominated markout reference prices captured from the pricing
// store at fixed horizons after the fill (0s/1s/5s/15s/30s). Populated only
// by GetFills (the historical query joins `fill_markouts`); always unset on
// the SubscribeFills stream, where markouts don't exist yet. A horizon is
// unset when no price was captured (store too stale, or low-timeframe
// horizons disabled).
Decimal ref_price_0s = 9;
Decimal ref_price_1s = 10;
Decimal ref_price_5s = 11;
Decimal ref_price_15s = 12;
Decimal ref_price_30s = 13;
Decimal notional_usd = 14;
}
// -- Per-maker on-chain activity -------------------------------------
// A single on-chain account action performed by the authenticated maker:
// collateral deposit / withdrawal, maker-account creation, or joining a
// spot market. Trading fills are served separately by GetFills /
// SubscribeFills and are not included here.
// Selects which `MakerActivity` actions GetActivity returns; `UNSPECIFIED` is
// ignored. Each value maps to one action case, except `DEPOSIT_WITHDRAW` which
// covers both `deposit` and `withdraw`.
enum MakerActivityType {
MAKER_ACTIVITY_TYPE_UNSPECIFIED = 0;
MAKER_ACTIVITY_TYPE_DEPOSIT_WITHDRAW = 1;
MAKER_ACTIVITY_TYPE_ADD_MARKET = 2;
MAKER_ACTIVITY_TYPE_MANAGE_MAKER = 3;
MAKER_ACTIVITY_TYPE_SET_CROSS_SPREAD = 4;
MAKER_ACTIVITY_TYPE_SET_QUOTING_PARAMS = 5;
}
message MakerActivity {
uint64 slot = 1;
Timestamp ts = 2;
// Base58 transaction signature the action was emitted in.
string signature = 3;
// 6 / create_maker retired: maker creation is archived but not surfaced here.
reserved 6;
reserved "create_maker";
oneof action {
DepositAction deposit = 4;
WithdrawAction withdraw = 5;
AddMarketAction add_market = 7;
ManageMakerAction manage_maker = 8;
CrossSpreadAction set_cross_spread = 9;
QuotingParamsAction set_quoting_params = 10;
}
}
// Collateral deposited into a spot balance.
message DepositAction {
SpotId spot_id = 1;
// Token units deposited (server scales by SpotMetadata.decimals).
Decimal amount = 2;
// Spot balance after the deposit, token units.
Decimal balance_after = 3;
}
// Collateral withdrawn from a spot balance.
message WithdrawAction {
SpotId spot_id = 1;
// Token units withdrawn (server scales by SpotMetadata.decimals).
Decimal amount = 2;
// Spot balance after the withdrawal, token units.
Decimal balance_after = 3;
}
// The maker joined a spot market by adding a micro-book, enabling it to
// quote there. `book_index` / `soft_max_balance` are absent on actions
// indexed before those fields were recorded.
message AddMarketAction {
SpotId spot_id = 1;
optional uint32 book_index = 2;
// Soft balance cap configured for the book, token units.
Decimal soft_max_balance = 3;
}
// A change to a maker's authorities via the on-chain ManageMaker instruction:
// adding/removing a quoting authority, or rotating the admin / withdraw
// authority. The program emits no event for these - the server recovers them
// from the instruction data.
message ManageMakerAction {
enum Kind {
KIND_UNSPECIFIED = 0;
ADD_QUOTER = 1;
REMOVE_QUOTER = 2;
SET_ADMIN = 3;
SET_WITHDRAW_AUTHORITY = 4;
}
Kind kind = 1;
// The authority pubkey added, removed, or set (base58).
string authority = 2;
}
// A maker changed a cross-market spread on one of its micro books, via the
// on-chain UpdateQuotingParams instruction (which emits no event). The spread
// is applied to the fair price when the `market_spot` book quotes against
// `cross_spot`.
//
// Reflects the cross-spread change as *submitted* in the instruction. The
// program applies a quoting-params update only when its order sequence is
// fresh, so in the rare case a stale (out-of-order) refresh carries a
// cross-spread change, this may report a change the chain skipped.
message CrossSpreadAction {
// The micro book's own market.
SpotId market_spot = 1;
// The cross leg the spread applies to.
SpotId cross_spot = 2;
// Raw on-chain spread (oracle units); absent means the cross spread was
// cleared (no cross spread).
optional uint32 spread = 3;
}
// A maker changed quoting params (enable flag and/or soft cap) on a micro book,
// via UpdateQuotingParams (no CPI event). Both fields are sent on every update,
// so each value is reported rather than just what changed. Recovered only for
// params-only updates (`quoting` unset, no cross-spread); reflects the submitted
// values, which a stale order sequence may mean the chain skipped.
message QuotingParamsAction {
SpotId spot_id = 1;
// Whether the book is enabled for quoting (`false` = paused).
bool enabled = 2;
// Soft balance cap, token units; absent means no cap.
Decimal soft_max_balance = 3;
}
// -- Stats messages (stats/messages.proto) --------------------------------------------------------
// -- Rolling-window enums --------------------------------------------
enum StatsWindow {
STATS_WINDOW_UNSPECIFIED = 0;
STATS_WINDOW_1H = 1;
STATS_WINDOW_24H = 2;
STATS_WINDOW_30D = 3;
}
// -- Shared building blocks ------------------------------------------
message WindowStats {
StatsWindow window = 1;
Decimal volume_base = 2;
Decimal volume_quote = 3;
uint64 fill_count = 4;
Decimal volume_usd = 5;
}
// Cross-venue totals are USD: base and quote assets differ per pair and do
// not add.
message CrossVenueWindowStats {
reserved 2;
StatsWindow window = 1;
uint64 fill_count = 3;
// Valued when each fill was ingested, so price moves never restate it.
// Excludes fills that were ingested unpriced.
Decimal volume_usd = 4;
}
message PairVolume {
Pair pair = 1;
WindowStats one_hour = 2;
WindowStats twenty_four_hour = 3;
WindowStats thirty_day = 4;
Decimal last_price = 5;
}
// -- GetSummary ------------------------------------------------------
message GetSummaryRequest {}
message GetSummaryResponse {
reserved 6, 7;
uint32 active_pairs = 1;
uint32 active_makers = 2;
CrossVenueWindowStats one_hour = 3;
CrossVenueWindowStats twenty_four_hour = 4;
CrossVenueWindowStats thirty_day = 5;
uint64 fill_count_all_time = 8;
Decimal volume_usd_all_time = 14;
// Distinct taker accounts in the last 24h.
uint32 unique_traders_24h = 9;
// USDC-quoted trades only.
LargestTrade largest_trade_24h = 10;
UpdateRate update_rate = 11;
UpdateRate oracle_update_rate = 12;
// Sampled periodically, not real-time.
Decimal total_nav = 13;
}
message LargestTrade {
Pair pair = 1;
Decimal size = 2;
Decimal price = 3;
Timestamp ts = 4;
Decimal notional = 5;
}
// -- GetVolumeBreakdown ----------------------------------------------
message GetVolumeBreakdownRequest {
// Empty = every pair with fills in the last 30d.
repeated Pair pairs = 1;
}
message GetVolumeBreakdownResponse {
// Ordered by 24h volume descending.
repeated PairVolume pairs = 1;
}
// -- GetVolumeSeries -------------------------------------------------
message GetVolumeSeriesRequest {
// Absent = cross-venue aggregate.
Pair pair = 1;
// Sets horizon and bucket size: 1H -> 60 x 1m, 24H -> 288 x 5m,
// 30D -> 720 x 1h.
StatsWindow window = 2;
}
message VolumeBucket {
// Bucket start (inclusive).
Timestamp ts = 1;
// Per-pair only; absent on a cross-venue request, where `volume_usd`
// carries the total.
Decimal volume_base = 2;
Decimal volume_quote = 3;
uint64 fill_count = 4;
Decimal volume_usd = 5;
}
message GetVolumeSeriesResponse {
// Ordered oldest-first.
repeated VolumeBucket buckets = 1;
uint32 bucket_seconds = 2;
}
// -- GetAssetTvls ----------------------------------------------------
message GetAssetTvlsRequest {}
message AssetTvl {
SpotMetadata asset = 1;
// Token units, not atoms.
Decimal total_balance = 2;
uint32 maker_count = 3;
}
message GetAssetTvlsResponse {
repeated AssetTvl assets = 1;
}
// -- UpdateRate ------------------------------------------------------
message UpdateRate {
double per_second_1s = 1;
double per_second_1m = 2;
double per_second_5m = 3;
}
// -- Node messages (node/messages.proto) --------------------------------------------------------
// -- GetNodeInfo -----------------------------------------------------
message GetNodeInfoRequest {}
message GetNodeInfoResponse {
// Server build identity.
string version = 1;
string git_commit = 2;
// Schema this node was built against, so a client can detect skew
// against its own generated stubs.
string proto_hash = 3;
string proto_rev = 4;
// Solana cluster this node is pointed at (e.g. "mainnet-beta").
string cluster = 5;
// Deploy region owning this node. Matches the `region` reported on
// maker_admin streams.
string region = 6;
// Base58 program id of the on-chain venue this node serves.
string program_id = 7;
// Process start time. Absolute, so it needs no fetch-time anchor to
// interpret; uptime is `now - started_at`.
Timestamp started_at = 8;
LeaderProximity leader_proximity = 9;
// Per-process id of this flint-server instance. Same id space as
// `QuoterSession.server_instance`.
string server_instance = 10;
// Approximate location of `region`. Absent for unmapped regions (e.g.
// local/dev); treat the precision as illustrative.
optional LatLng region_lat_lng = 11;
// This node only - whether it is keeping up and able to serve. Venue and
// per-pair health are a separate axis, carried on `StatusEvent`.
HealthState health = 12;
// Why `health` is not HEALTH_STATE_HEALTHY. Empty when it is.
string health_reason = 13;
}
// How close this node sits to the block leaders it sends to, measured from
// observed round trips. A client weighing a direct TPU send off
// `TxService.SubscribeLeaders` against the extra hop through `SubmitTx` can
// compare its own leader distance to this.
message LeaderProximity {
// Leaders included in the measurement. `0` means no sample yet, in which
// case the distance fields are meaningless.
uint32 measured = 1;
uint32 mean_distance_micros = 2;
uint32 median_distance_micros = 3;
uint32 p90_distance_micros = 4;
}
// -- ListNodes -------------------------------------------------------
message ListNodesRequest {
// Empty = every cluster the deployment serves. Set to list only the
// endpoints on one cluster (e.g. "mainnet-beta").
string cluster = 1;
}
// One dialable flint-server endpoint.
message NodeEndpoint {
// gRPC endpoint to dial, host:port or a full URL.
string endpoint = 1;
// Deploy region. Same id space as `GetNodeInfoResponse.region`.
string region = 2;
// Approximate location of `region`. Absent for unmapped regions (e.g.
// local/dev); treat the precision as illustrative.
optional LatLng region_lat_lng = 3;
// Solana cluster this endpoint serves.
string cluster = 4;
// True for the endpoint serving this request.
bool current = 5;
}
message ListNodesResponse {
// Every endpoint the deployment publishes, including `current`. Carries no
// health: an entry means "published", not "up right now". A client that
// needs liveness calls GetNodeInfo on the endpoint it picked.
repeated NodeEndpoint nodes = 1;
}
// -- Maker messages (maker/messages.proto) --------------------------------------------------------
// -- MakerService request / response ---------------------------------
message SubscribeBalanceRequest {
// Optional per-spot filter. Empty = every spot the authenticated maker
// has a balance in (base + quote of every pair they quote on).
repeated SpotId spot_ids = 1;
}
message GetBalanceRequest {
// Optional per-spot filter; empty = all spots.
repeated SpotId spot_ids = 1;
}
message GetBalanceResponse {
repeated MakerBalanceEvent balances = 1;
}
message SubscribeMakerMarketSnapshotsRequest {}
// Per-maker volume breakdown. The authenticated maker_id is taken from
// the bearer token; clients only optionally narrow the pair set.
message GetMakerVolumeBreakdownRequest {
// Optional pair filter. Empty = every pair the maker has fills in
// across the largest window (30d).
repeated Pair pairs = 1;
}
message GetMakerVolumeBreakdownResponse {
// Ordered by 24h volume descending.
repeated PairVolume pairs = 1;
}
message SubscribeMakerFillsRequest {
// Optional per-pair filter. Empty = every pair the authenticated maker
// gets a fill on.
repeated Pair pairs = 1;
}
// SEC_<n> rather than <n>S: prefix-stripping codegen (prost) would turn
// MARKOUT_HORIZON_0S into the invalid identifier `0S`. SEC_0 / SEC_1 are only
// populated when the server runs with MARKOUT_LOW_TIMEFRAME_ENABLE, off in
// mainnet.
enum MarkoutHorizon {
MARKOUT_HORIZON_UNSPECIFIED = 0; // server default: 5s
MARKOUT_HORIZON_SEC_0 = 1;
MARKOUT_HORIZON_SEC_1 = 2;
MARKOUT_HORIZON_SEC_5 = 3;
MARKOUT_HORIZON_SEC_15 = 4;
MARKOUT_HORIZON_SEC_30 = 5;
}
enum MakerFillOrder {
MAKER_FILL_ORDER_UNSPECIFIED = 0; // same as TIME_DESC
MAKER_FILL_ORDER_TIME_DESC = 1;
// Largest absolute USD markout first; fills without one sort last. A biased
// slice of the window's extremes, not a sample - derive nothing aggregate
// from it, use GetMarkoutSummary.
MAKER_FILL_ORDER_ABS_MARKOUT_DESC = 2;
}
message GetMakerFillsRequest {
// Optional per-pair filter. Empty = every pair the authenticated maker
// got a fill on.
repeated Pair pairs = 1;
// Inclusive lower bound. Unset / zero-micros = server retention window.
Timestamp start = 2;
// Exclusive upper bound. Unset / zero-micros = now.
Timestamp end = 3;
// Max rows to return. Zero = server default (50). Hard cap 1000.
uint32 limit = 4;
// Ordering for the returned page. Unset = TIME_DESC.
MakerFillOrder order_by = 5;
// Horizon ABS_MARKOUT_DESC ranks on. Ignored for TIME_DESC.
MarkoutHorizon markout_horizon = 6;
}
message GetMakerFillsResponse {
// Ordered per the request's `order_by`; newest-first by default.
repeated MakerFillEvent fills = 1;
}
// -- GetMarkoutSummary -----------------------------------------------
// Markout over the whole window. GetFills is capped at 1000 rows, so totals
// summed from it describe only a busy maker's most recent slice; these have
// no such cap.
message GetMakerMarkoutSummaryRequest {
repeated Pair pairs = 1;
// Inclusive lower bound. Unset / zero-micros = server retention window.
Timestamp start = 2;
// Exclusive upper bound. Unset / zero-micros = now.
Timestamp end = 3;
MarkoutHorizon horizon = 4;
}
// A rolling 24h window from `effective_start`, not a calendar day - no
// timezone is involved.
message MarkoutBucket {
uint32 index = 1;
double net = 2;
double adverse = 3;
double favorable = 4;
}
message MarkoutMarketRow {
Pair pair = 1;
double net = 2;
double adverse = 3;
double favorable = 4;
double volume_usd = 5;
uint64 fills = 6;
uint64 fills_with_markout = 7;
optional double avg_bps = 8;
}
// Edges travel with the data so clients render labels from the response
// rather than hardcoding the bucket list. Unset = unbounded.
message MarkoutHistogramBin {
optional double min = 1;
optional double max = 2;
uint64 count = 3;
double volume_usd = 4;
double net = 5;
}
message GetMakerMarkoutSummaryResponse {
double net = 1;
// Absolute USD of the negative-markout fills only (adverse selection).
double adverse = 2;
double favorable = 3;
// At-ingest USD notional over every priced fill.
double volume_usd = 4;
// Every fill in the window, including those behind no USD figure.
uint64 fills = 5;
// Carrying both a USD notional and a reference price - the only fills
// behind net / adverse / favorable.
uint64 fills_with_markout = 6;
// Ingested without a USD notional, not reconstructible later. Non-zero
// means every USD total here is a lower bound.
uint64 fills_unpriced = 7;
uint64 fills_missing_ref = 8;
optional double avg_bps = 9;
// Unset, not 0, when no fill carried a markout - matching avg_bps.
optional double avg_per_fill = 10;
repeated MarkoutBucket buckets = 11;
// Descending by `adverse`.
repeated MarkoutMarketRow markets = 12;
// Ascending by bps, edges contiguous.
repeated MarkoutHistogramBin histogram = 13;
// Window actually queried. A longer range comes back clamped to the
// server's 30d retention rather than rejected, so label the range from
// these rather than from the request.
Timestamp effective_start = 14;
Timestamp effective_end = 15;
}
// Per-maker volume series. The authenticated maker_id is taken from the
// bearer token. Window selects both horizon and bucket width identically
// to `StatsService.GetVolumeSeries`.
message GetMakerVolumeSeriesRequest {
// Optional pair filter. Absent = aggregate across every pair the maker
// has fills in.
Pair pair = 1;
StatsWindow window = 2;
}
message GetMakerVolumeSeriesResponse {
// Ordered oldest-first.
repeated VolumeBucket buckets = 1;
uint32 bucket_seconds = 2;
}
// -- GetNavHistory ----------------------------------------------
// Per-maker historical NAV (net asset value, USD). The authenticated
// maker_id is taken from the bearer token. Window selects both horizon and
// bucket width identically to `GetVolumeSeries`.
message GetNavHistoryRequest {
StatsWindow window = 1;
}
message NavPoint {
Timestamp ts = 1;
// Total USD value of the maker's holdings in this bucket. Held assets with
// no available price are valued at 0; `unpriced_assets` flags when that
// makes the total a partial valuation.
Decimal nav_usd = 2;
// Freshness of the prices the NAV was computed from. Lets clients spot a
// valuation taken against carried-forward (stale) prices.
Timestamp price_as_of = 3;
// Held assets that were priced into `nav_usd`.
uint32 priced_assets = 4;
// Held assets with no price, counted as 0 in `nav_usd`. Non-zero means the
// total understates true NAV.
uint32 unpriced_assets = 5;
}
message GetNavHistoryResponse {
// Ordered oldest-first.
repeated NavPoint points = 1;
uint32 bucket_seconds = 2;
}
// -- GetStats --------------------------------------------------------
message GetMakerStatsRequest {}
message GetMakerStatsResponse {
reserved 2, 3;
// Echoed from the bearer token so the client can sanity-check the scope.
MakerId maker_id = 1;
// Match events where this maker provided liquidity.
UpdateRate fills = 4;
// Transactions accepted from this maker by the server's TxService
// (counts every TX whose initial outcome was `Submitted`).
UpdateRate transactions_sent = 5;
// Transactions confirmed on-chain (TxOutcome::Landed).
UpdateRate transactions_landed = 6;
// Transactions that failed - rejected pre-submit, expired before landing,
// or landed with an error.
UpdateRate transactions_failed = 7;
// Venue-wide oracle-fair update rate. Same value every maker observes -
// included so MMs can compare their request rate against oracle volatility.
UpdateRate oracle_updates = 8;
}
// -- GetActivity -----------------------------------------------------
// Per-maker on-chain account-action history. The authenticated maker_id is
// taken from the bearer token / API key. Covers deposits, withdrawals,
// maker-account creation, and market joins - not trading fills (see GetFills).
message GetMakerActivityRequest {
// Inclusive lower bound. Unset / zero-micros = server retention window.
Timestamp start = 1;
// Exclusive upper bound. Unset / zero-micros = now.
Timestamp end = 2;
// Max rows to return. Zero = server default (50). Hard cap 1000.
uint32 limit = 3;
// Optional action-type filter. Empty = all types; unrecognized / UNSPECIFIED
// entries are ignored, and a filter matching no known type yields no rows.
repeated MakerActivityType types = 4;
}
message GetMakerActivityResponse {
// Ordered newest-first.
repeated MakerActivity activities = 1;
}
// -- GetDecodedTransaction (in-house explorer) -----------------------
//
// Signature-first read backing the in-house transaction explorer. Returns only
// transactions the indexer DECODED into the `events` table (Flint-program txs),
// not arbitrary Solana transactions. Unlike the other MakerService RPCs it is
// authenticated for access control but NOT maker-scoped: a transaction is
// inherently multi-party (a fill has a maker and a taker/counterparty), so the
// response carries every decoded event in the transaction regardless of which
// maker they belong to. Assembled from the already-decoded `events`
// (bloom-indexed on signature) plus tx-level fields from `raw_transactions`.
// Signatures the indexer never saw (non-Flint txs) return NOT_FOUND - the
// client then falls back to an external explorer link.
// Landing status of a transaction. Everything the indexer archived landed on
// chain; `FAILED` means it landed but reverted (recovered from archived tx
// meta). Rows indexed before tx-meta capture landed report `CONFIRMED` with an
// unset `fee_lamports`.
enum TransactionStatus {
TRANSACTION_STATUS_UNSPECIFIED = 0;
TRANSACTION_STATUS_CONFIRMED = 1;
TRANSACTION_STATUS_FAILED = 2;
}
// The kind of decoded program event a `DecodedEvent` carries. Mirrors the
// indexer's `EventKind` - CPI-event-backed kinds plus synthetic kinds the
// indexer recovers from instruction data. Tells the client how to render the
// row and which dashboard page to deep-link to (e.g. MATCH -> the maker page for
// the market; QUOTING_PARAMS_UPDATE -> quoter detail).
enum DecodedEventKind {
DECODED_EVENT_KIND_UNSPECIFIED = 0;
// Event-backed (1:1 with the on-chain SweetSpotEvent):
DECODED_EVENT_KIND_MATCH = 1;
DECODED_EVENT_KIND_TRADE_SETTLEMENT = 2;
DECODED_EVENT_KIND_CREATE_SPOT = 3;
DECODED_EVENT_KIND_CREATE_MAKER = 4;
DECODED_EVENT_KIND_ADD_MICRO_BOOK = 5;
DECODED_EVENT_KIND_DEPOSIT_WITHDRAW = 6;
DECODED_EVENT_KIND_CREATE_GLOBAL_ADMIN = 7;
DECODED_EVENT_KIND_SWAP = 8;
DECODED_EVENT_KIND_CROSS_CANCEL = 9;
DECODED_EVENT_KIND_CROSS_FILL = 10;
DECODED_EVENT_KIND_WITHDRAW_FEES = 11;
DECODED_EVENT_KIND_SET_GLOBAL_ADMIN = 12;
// Synthetic (recovered from instruction data, no CPI event):
DECODED_EVENT_KIND_MANAGE_MAKER = 13;
DECODED_EVENT_KIND_CROSS_SPREAD_UPDATE = 14;
DECODED_EVENT_KIND_QUOTING_PARAMS_UPDATE = 15;
}
// One decoded Flint-program event within a transaction - a single row of the
// indexer's `events` table. Optional fields are unset when the column is null
// for that event kind (e.g. a CreateSpot carries no side/price/size). Full
// type-specific detail rides as JSON in `payload_json`; `kind` says how to read
// it. The `pair` / `maker_id` / `counterparty` fields are what make each row
// deep-linkable in the dashboard.
message DecodedEvent {
DecodedEventKind kind = 1;
// Trading pair the event touches; unset for venue-global events.
Pair pair = 2;
// The maker whose liquidity/account the event concerns; unset when not
// applicable. Deep-links to the maker page.
MakerId maker_id = 3;
// The other party, meaning depends on kind: the taker's signer pubkey for a
// MATCH, the opposing maker for a CROSS_FILL, the mint for a CREATE_SPOT
// (base58). Unset when not applicable.
optional string counterparty = 4;
// Taker side for fills; SIDE_UNSPECIFIED when the event has no side.
Side side = 5;
// Price / size of the fill. `Decimal` is a message, so these carry field
// presence: they are left UNSET (absent on the wire -> null client-side, not a
// zero-value) for non-trading event kinds like CREATE_SPOT / CREATE_MAKER /
// ADD_MICRO_BOOK / WITHDRAW_FEES, mirroring the indexer's `Option<Decimal>`.
// No `optional` keyword is needed - that only adds presence to bare scalars.
Decimal price = 6;
Decimal size = 7;
// On-chain order id (formatted) when the event references one.
optional string order_id = 8;
// Position within the transaction, used to order rows and address the row
// uniquely (mirrors GetQuote's addressing).
uint32 instruction_index = 9;
uint32 event_index = 10;
// Signing quoting-authority pubkey (base58) for quote-bearing events;
// populated once the quote-tape join lands (stats iteration), unset until
// then. Deep-links to quoter detail.
optional string quoter = 11;
// Raw decoded SweetSpotEvent as JSON, for type-specific detail rendering.
string payload_json = 12;
}
// Address a transaction by its base58 signature.
message GetDecodedTransactionRequest {
string signature = 1;
}
message GetDecodedTransactionResponse {
// Base58 signature (echoed).
string signature = 1;
uint64 slot = 2;
// Chain (block) time of the transaction.
Timestamp block_time = 3;
TransactionStatus status = 4;
// Transaction fee in lamports. Unset when tx meta was not archived for this
// transaction (rows indexed before tx-meta capture landed).
optional uint64 fee_lamports = 5;
// Decoded Flint-program events, ordered by (instruction_index, event_index).
repeated DecodedEvent events = 6;
}
// -- SDK access ------------------------------------------------------
message GetSdkCredentialsRequest {}
message GetSdkCredentialsResponse {
// Plaintext registry token (read-only pull scope). Stable: every call
// returns the same token, so clients never need to store it.
string token = 1;
// Server-assigned token name, e.g. "sdk-maker-12-mainnet".
string token_name = 2;
// Cargo sparse-index URL for `[registries.<registry_name>]` in
// .cargo/config.toml.
string index_url = 3;
// Registry name for Cargo.toml / .cargo/config.toml, e.g. "kellnr".
string registry_name = 4;
// When the token was first minted.
Timestamp created_at = 5;
}
// -- Maker admin messages (maker_admin/messages.proto) --------------------------------------------------------
// Aggregate of one quoting authority's currently-open sessions.
message QuotingAuthority {
// Quoting keypair pubkey (base58) that authenticated the sessions.
string quoter = 1;
// Number of open session streams across all instances.
uint32 open_sessions = 2;
// Most recent heartbeat seen for any of this authority's sessions.
Timestamp last_seen = 3;
// Best (lowest) live round-trip across this authority's sessions, micros.
// Zero when no session has completed a heartbeat round trip yet.
uint64 min_rtt_micros = 4;
// Distinct flint-server regions this authority is connected to.
repeated string regions = 5;
// Distinct client SDK build strings observed.
repeated string sdks = 6;
// Most recent time a quote (SubmitTx) from this authority was received by the
// server. Absent if none seen recently.
optional Timestamp last_submitted_at = 7;
// Most recent time a tx from this authority's keypair confirmed on-chain.
// Absent if none landed recently. The gap from `last_submitted_at` shows
// whether submitted quotes are actually landing.
optional Timestamp last_landed_at = 8;
// Lamport balance of the quoting keypair - the account paying this
// authority's transaction fees, so a falling balance is a quoting outage in
// waiting. Lamports, not SOL: the balance is an exact integer on chain and
// stays one here (divide by 1e9 to display). Absent when the balance has
// not been read yet, or when the RPC lookup for it failed - absent means
// "unknown", never "zero".
optional uint64 sol_lamports = 9;
// When `sol_lamports` was read. The balance is polled rather than streamed,
// so this is how stale the number is. Absent whenever `sol_lamports` is.
optional Timestamp sol_balance_as_of = 10;
// Heartbeat round-trip distribution across this authority's sessions over
// the last 5 minutes, micros. `min_rtt_micros` is the best live round trip;
// these are the recent shape of it, and p99 is where a quoter actually
// feels the tail. Both zero when `rtt_samples` is zero.
uint64 p50_rtt_micros = 11;
uint64 p99_rtt_micros = 12;
// Round trips the two percentiles were computed from. `0` means no session
// completed a heartbeat in the window and the percentiles are meaningless.
uint32 rtt_samples = 13;
// Recent history behind the point-in-time fields above: latency, quote
// rate, and transaction outcomes in fixed buckets on one shared x axis, so
// a latency spike can be read against the quoting and landing alongside it.
// One bucket type rather than three parallel series so the three charts are
// always sampled over identical boundaries.
//
// Ordered oldest-first, with every bucket in the window present including
// the empty ones, so a client can plot the array directly without
// reconstructing gaps. Buckets are aligned to wall-clock multiples of
// `ListQuotingAuthoritiesResponse.bucket_seconds`, which makes the newest
// one partial. Always populated - an empty entry means the authority was
// silent for that bucket, never that the series was omitted.
//
// Keyed on the authority (the keypair), not on a session: sessions come and
// go mid-window, and a quoter that reconnects has not stopped quoting.
repeated QuoterSeriesBucket series = 14;
}
// One currently-open session stream.
message QuoterSession {
// Per-stream id minted by the server at connect.
string session_id = 1;
// Quoting keypair pubkey (base58).
string quoter = 2;
Timestamp connected_at = 3;
// Most recent heartbeat round trip for this session.
Timestamp last_seen = 4;
// Peer IP as seen by the server (proxy-forwarded first hop when present).
string ip = 5;
string sdk = 6;
string proto_rev = 7;
// Latest heartbeat round-trip time, micros (0 before the first round trip).
uint64 rtt_micros = 8;
// Latest estimated client clock drift, micros (signed; 0 if unknown).
int64 drift_micros = 9;
// flint-server deploy region that owns this stream.
string region = 10;
// Per-process id of the flint-server instance that owns this stream.
string server_instance = 11;
// Approximate location of the connecting quoter, derived from `ip`. Absent
// until a GeoIP city database is wired in; treat precision as illustrative.
optional LatLng session_lat_lng = 12;
// Approximate location of the flint-server `region`. Absent for unmapped
// regions (e.g. local/dev).
optional LatLng server_lat_lng = 13;
// Most recent time this session's quoter submitted a quote (SubmitTx) to the
// server. Per quoter key, so all of a quoter's sessions share the value.
optional Timestamp last_submitted_at = 14;
// Most recent time a tx from this session's quoter keypair confirmed
// on-chain. Per quoter key. The gap from `last_submitted_at` shows whether
// submitted quotes are landing.
optional Timestamp last_landed_at = 15;
}
message ListQuotingAuthoritiesRequest {
// Optional: restrict to a single quoting authority (pubkey), for the
// quoter-detail page. Empty returns every one of the caller's authorities.
// Same filter semantics as `ListQuoterSessionsRequest.quoter`.
string quoter = 1;
}
message ListQuotingAuthoritiesResponse {
// Ordered by open-session count, descending.
repeated QuotingAuthority authorities = 1;
// Window and resolution of the `series` carried on every authority above.
// One window per response rather than per row, so every authority's charts
// share an x axis and are directly comparable.
//
// Bucket width, echoed rather than assumed. Currently a fixed 5 minutes at
// 30-second buckets (10 buckets); read the width from here and the horizon
// from `series_start`/`series_end` rather than hard-coding either, so a
// later change of resolution is not a client break.
uint32 bucket_seconds = 2;
// `series_start` inclusive, `series_end` exclusive.
Timestamp series_start = 3;
Timestamp series_end = 4;
}
message ListQuoterSessionsRequest {
// Optional: restrict to a single quoting authority (pubkey). Empty returns
// all of the caller's open sessions.
string quoter = 1;
}
message ListQuoterSessionsResponse {
// Ordered most-recently-seen first.
repeated QuoterSession sessions = 1;
}
// One bucket of a quoting authority's activity.
message QuoterSeriesBucket {
// Bucket start (inclusive).
Timestamp ts = 1;
// -- Latency -------------------------------------------------------
// Heartbeat round trips completed within the bucket, across all of the
// authority's sessions. `0` means the authority was idle or disconnected
// for the bucket and the two percentiles below are meaningless - that is a
// gap in the latency line, not a drop to zero.
uint32 rtt_samples = 2;
uint64 p50_rtt_micros = 3;
uint64 p99_rtt_micros = 4;
// -- Quotes --------------------------------------------------------
// Quote updates the server received from this authority in the bucket,
// counted the same way `SubscribeQuotes` emits them (one per quoted leg).
uint64 quotes = 5;
// -- Transactions --------------------------------------------------
// Bucketed by when the outcome was observed, not by when the transaction
// was submitted. A tx sent near the end of one bucket usually lands in the
// next, so `transactions_landed` in a bucket does not partition
// `transactions_sent` in the same bucket - the two lines are rates to
// compare, not a breakdown to sum.
//
// Transactions accepted from this authority by TxService (initial outcome
// `Submitted`).
uint64 transactions_sent = 6;
// Confirmed on-chain.
uint64 transactions_landed = 7;
// Rejected pre-submit, expired before landing, or landed with an error.
uint64 transactions_failed = 8;
// -- Landing latency -----------------------------------------------
// Submit-to-land time for this authority's transactions, micros: from when
// TxService accepted the tx to when it was confirmed on-chain. Bucketed by
// when the landing was observed, matching `transactions_landed`, so a
// sample here belongs to a tx that may have been sent in an earlier bucket.
//
// Landings the two percentiles were computed from. `0` means nothing landed
// in the bucket and the percentiles are meaningless - a gap in the line,
// not a drop to zero. Counts only landings whose submit time is known, so
// it can trail `transactions_landed`; it never exceeds it.
uint32 land_samples = 9;
uint64 p50_land_micros = 10;
uint64 p99_land_micros = 11;
}
// -- Tx events (tx/events.proto) --------------------------------------------------------
// -- Chain-tip data --------------------------------------------------
message BlockhashEvent {
Blockhash blockhash = 1;
// Server's current priority-fee recommendation (microlamports per CU)
// sampled from recent landed txs. Zero = no recommendation yet.
uint64 recommended_cu_price = 2;
Timestamp ts = 3;
// Block height at which this blockhash expires (Solana
// lastValidBlockHeight). A tx using it can no longer land once the chain's
// block height passes this value. Lets clients compute the exact remaining
// validity of each broadcast hash and pick one matching a desired lifetime.
// Zero before the server's height clock has warmed up.
uint64 last_valid_block_height = 4;
}
message SlotEvent {
uint64 slot = 1;
Timestamp ts = 2;
}
// One upcoming leader and the TPU socket to reach it on, so a client can
// send a tx straight at the validator that will produce the block instead of
// paying the extra hop through `SubmitTx`.
//
// Direct send is a latency optimization, not a replacement for `SubmitTx`:
// leaders throttle QUIC connections from unstaked peers, so a client with no
// stake-weighted path should treat these sends as best-effort and keep
// `SubmitTx` as the path it relies on for landing.
//
// Events are idempotent and keyed by `start_slot`. On connect the server
// replays every leader currently inside its lookahead window, then pushes
// each new one as the schedule advances, so a fresh or restarted client is
// never blind. Each event self-expires once the chain passes `end_slot`;
// there are no cancel messages. Contact info can change mid-epoch (a
// validator restarting on a new address), in which case the server re-sends
// the same `start_slot` with updated fields - last write wins.
message LeaderEvent {
// Inclusive first slot of this leader's window.
uint64 start_slot = 1;
// Inclusive last slot of this leader's window. Solana assigns leaders in
// runs of 4 consecutive slots, so this is normally `start_slot + 3`; it is
// sent explicitly rather than assumed so consecutive runs by the same
// validator can be coalesced into one event.
uint64 end_slot = 2;
// TPU address as advertised in the validator's gossip contact info.
// Dotted-quad IPv4 today; typed as a string so an IPv6 leader does not
// require a wire change.
string tpu_ip = 3;
// QUIC TPU port - the one to send to. Zero when the leader advertises no
// QUIC TPU socket, in which case the client must not attempt a direct send
// and should fall back to `SubmitTx` for that window.
uint32 tpu_quic_port = 4;
// Server send time, for client-side latency attribution. Not policy.
Timestamp ts = 5;
}
// -- Per-tx lifecycle ------------------------------------------------
// Server has accepted the tx for submission (pre-confirmation).
message TxAckEvent {
// Base58 transaction signature.
string signature = 1;
Timestamp ts = 2;
}
// Tx observed on-chain at Processed commitment with err == None.
// Not Solana "confirmed" or "finalized" commitment - reserves that name
// for a future supermajority-stake signal.
message TxLandedEvent {
// Base58 transaction signature.
string signature = 1;
uint64 slot = 2;
Timestamp ts = 3;
}
// Tx landed but reverted, or timed out before landing.
message TxFailedEvent {
// Base58 transaction signature.
string signature = 1;
string reason = 2;
Timestamp ts = 3;
}
// Multiplexed tx-status stream envelope.
message TxStatusEvent {
oneof kind {
TxAckEvent ack = 1;
TxLandedEvent landed = 2;
TxFailedEvent failed = 3;
}
}
// -- Tx messages (tx/messages.proto) --------------------------------------------------------
// -- TxService request / response ------------------------------------
// Submit a fully signed Solana transaction. The server forwards the tx
// to its upstream RPC/jito paths; lifecycle events (ack/landed/failed)
// arrive on `SubscribeTxStatus`.
message SubmitTxRequest {
// Serialized signed transaction bytes - legacy or v0. The server does
// not modify the tx; fee payer, recent blockhash, compute budget, and
// priority fee are all the client's choices.
bytes transaction = 1;
// Client wall-clock immediately before this request went out. Purely
// informational: the server never validates it, never rejects on it, and
// never reflects it back - it only feeds server-side latency telemetry for
// the inbound leg. Omit (or leave zero) if the client has no useful clock.
Timestamp client_sent_at = 2;
}
message SubmitTxResponse {
// Parsed base58 signature from the submitted tx. Use this to correlate with
// subsequent TxAckEvent / TxLandedEvent / TxFailedEvent received
// on `SubscribeTxStatus`.
string signature = 1;
// Server wall-clock timestamp the submission was accepted for queueing.
Timestamp ts = 2;
// Server wall-clock immediately before this response was written.
//
// `server_replied_at - ts` is the server's own handling time. Both stamps
// come off the same clock, so that difference is free of cross-machine
// skew; so is the client's own send-to-receive round trip, measured on the
// client's clock. Subtracting one from the other leaves network time. The
// client is the only place both halves exist, so the client does that math
// - the server publishes no end-to-end number.
Timestamp server_replied_at = 3;
}
// Retrieve the set of fee-payer pubkeys the server will pay tx fees for.
// Clients may use any returned pubkey as the `feePayer` of a submitted tx;
// the server then covers the SOL cost on landing.
message GetSponsoredPayersRequest {}
message GetSponsoredPayersResponse {
// Base58 fee-payer pubkeys.
repeated string payers = 1;
}
message SubscribeBlockhashRequest {}
message SubscribeSlotsRequest {}
// The server owns the lookahead depth. Kept as an empty message rather than
// no-arg so a future client-chosen lookahead or filter is an added field, not
// a new RPC.
message SubscribeLeadersRequest {}
// Server streams status transitions for any tx submitted under the
// authenticated maker's session. No client-side filter - the maker_id
// on the session token scopes the stream.
message SubscribeTxStatusRequest {}
// -- Spread protect events (spread_protect/events.proto) --------------------------------------------------------
// SpreadProtect stream event. The server can reset the client's local advisory
// state, then send idempotent widen windows that layer on top of the current
// default multiplier.
message SpreadProtectEvent {
oneof kind {
ResetState reset_state = 1;
WidenWindow widen_window = 2;
}
}
// Replace the client's local SpreadProtect state.
message ResetState {
// Baseline multiplier to apply when no widen window is active.
Decimal default_spread_mult = 1;
}
// An advisory request to widen the maker's own spread for a range of slots
// where network topology makes it harder to reprice in time (v0: bad leaders).
//
// The window is a slot range and self-expires at `end_slot`. There are no
// cancel messages and no separate validity field; messages are idempotent. If
// one is lost the maker simply does not widen for that window and the on-chain
// price band covers it.
//
// `spread_mult` is a multiplier on the maker's *own* spread, not an absolute
// bps value and not a score. Because the maker's spread already encodes pair
// volatility, the multiplier scales protection by vol for free and stays
// pair-agnostic, so the server never needs to know vol.
//
// Future versions add fields to this same message shape (e.g. a priority-fee
// decay slope or geo-latency term) without changing the consumer interface.
message WidenWindow {
// Inclusive first slot of the window.
uint64 start_slot = 1;
// Inclusive last slot of the window.
uint64 end_slot = 2;
// >= 1.0. Multiplier the maker MAY apply to its own spread for the window.
// Decimal string (e.g. "1.5") so the value is exact on the wire.
Decimal spread_mult = 3;
// Server send time, for client-side latency/attribution. Not policy.
Timestamp ts = 4;
}
// -- Spread protect messages (spread_protect/messages.proto) --------------------------------------------------------
// Subscribe to the SpreadProtect widen-window feed. No filters: the policy is
// server-side and the same windows apply to every maker.
message SubscribeSpreadProtectRequest {}
// -- Historical messages (historical/messages.proto) --------------------------------------------------------
// -- Historical primitives -------------------------------------------
enum CandleInterval {
CANDLE_INTERVAL_UNSPECIFIED = 0;
CANDLE_INTERVAL_1M = 1;
CANDLE_INTERVAL_5M = 2;
CANDLE_INTERVAL_15M = 3;
CANDLE_INTERVAL_30M = 4;
CANDLE_INTERVAL_1H = 5;
CANDLE_INTERVAL_4H = 6;
CANDLE_INTERVAL_1D = 7;
}
message Candle {
// Candle open time.
Timestamp ts = 1;
Decimal open = 2;
Decimal high = 3;
Decimal low = 4;
Decimal close = 5;
// Base-asset volume over the interval.
Decimal volume = 6;
}
// -- GetFills --------------------------------------------------------
message GetFillsRequest {
Pair pair = 1;
// Inclusive lower bound. Unset / zero-micros = unbounded (server uses
// its retention window).
Timestamp start = 2;
// Exclusive upper bound. Unset / zero-micros = now.
Timestamp end = 3;
// Max rows to return. Zero = server default (50). Hard cap 1000.
uint32 limit = 4;
}
message GetFillsResponse {
// Ordered newest-first.
repeated FillEvent fills = 1;
}
// -- GetCandles ------------------------------------------------------
message GetCandlesRequest {
Pair pair = 1;
CandleInterval interval = 2;
// Inclusive lower bound.
Timestamp start = 3;
// Exclusive upper bound. Unset / zero-micros = now.
Timestamp end = 4;
}
message GetCandlesResponse {
// Ordered oldest-first. Hard cap of 10_000 candles per response.
repeated Candle candles = 1;
}
// -- Escalation messages (escalation/messages.proto) --------------------------------------------------------
// -- EscalationService messages --------------------------------------
//
// Out-of-band alarm path for makers. When something looks wrong on the
// venue (stuck fills, balance drift, a suspected outage) a maker can page
// the on-call team directly rather than going through email/support. The
// page is fanned out to the team's alerting channel; this RPC confirms only
// that the page was accepted and dispatched, not that anyone has responded.
//
// This is the high-urgency path - it wakes the on-call. Low-urgency matters
// belong in Slack / Telegram, not here.
message PageTheTeamRequest {
// Free-text description of the problem. Required; the server rejects an
// empty or whitespace-only message with INVALID_ARGUMENT. Keep it short -
// it is delivered verbatim into the on-call alert.
string message = 1;
}
message PageTheTeamResponse {
// Server-minted id for this page, echoed in the on-call alert so the maker
// and the team can refer to the same incident.
string page_id = 1;
// When the server accepted and dispatched the page.
Timestamp received_at = 2;
}
// -- Quote messages (quote/messages.proto) --------------------------------------------------------
// The on-chain update kind a `QuoteUpdate` carries. Tells the client how to
// interpret `payload_json`. `None` (params-only) updates are never emitted as
// quotes, so there is no variant for them; UNSPECIFIED is the proto3 default
// and should not appear on a real quote.
enum QuoteUpdateKind {
QUOTE_UPDATE_KIND_UNSPECIFIED = 0;
// Explicit price levels (place/cancel deltas) - `payload_json`: {bids, asks}.
QUOTE_UPDATE_KIND_ORDERLIST = 1;
// Ladders of offsets from the oracle - `payload_json`: {risk, buy_orders, sell_orders}.
QUOTE_UPDATE_KIND_ORACLE_OFFSET = 2;
// Parametric distribution around the fair - `payload_json`: {risk, client_order_id, ...}.
QUOTE_UPDATE_KIND_LINEAR_DISTRIBUTION = 3;
// Fast-path oracle-offset fair refresh - `payload_json`: {last_oracle_slot, oracle_sequence, buy_fair, sell_fair}.
QUOTE_UPDATE_KIND_ORACLE_FAIR = 4;
// Fast-path linear-distribution fair refresh - `payload_json`: {last_oracle_slot, oracle_sequence, buy_start_price, ...}.
QUOTE_UPDATE_KIND_LINEAR_FAIR = 5;
}
// A single on-chain quote update (`UpdateQuotingParams` carrying a concrete
// strategy, or a fast-path fair refresh) for one maker on one market. Emitted
// live on the quote stream and returned by the history query in the same shape.
//
// Strategy detail is heterogeneous - orderlist levels, oracle offsets, or
// linear-distribution params - so it is carried as JSON in `payload_json`
// rather than a fixed schema. `strategy` says how to interpret it.
message QuoteUpdate {
// Maker's quoting spot; the market is this spot vs the global numeraire.
uint32 spot_id = 1;
// Block (chain) time of the quote.
Timestamp block_time = 2;
uint64 slot = 3;
// Transaction signature (base58) that carried the update.
string signature = 4;
// Index of the instruction within the tx, and of the param-leg within the
// instruction (one update can touch several spots).
uint32 instruction_index = 5;
uint32 leg_index = 6;
uint32 maker_id = 7;
// Signing quoting-authority pubkey (base58).
string quoter = 8;
// Which quote-update shape this record carries.
QuoteUpdateKind strategy = 9;
// The book's enable flag carried by this update. For fast-fair refreshes,
// this is always true because the instruction does not carry enable state.
bool enabled = 10;
// Soft inventory cap in atoms; max-uint64 is the no-cap sentinel. Fast-fair
// refreshes use the sentinel because the instruction does not carry cap state.
uint64 soft_max_balance = 11;
// Strategy-specific detail as JSON (levels / offsets / risk params).
string payload_json = 12;
}
// Live quote subscription filters, applied within the authenticated maker.
// Each repeated field is OR-within and the dimensions are AND-across; an empty
// field is a wildcard on that dimension.
message SubscribeQuotesRequest {
reserved 1; // was maker_ids - the RPC is scoped to the authenticated maker
// Markets (quoting spots) to include; empty = all of the maker's markets.
repeated uint32 spot_ids = 2;
// Signing quoter pubkeys (base58) to include; empty = all of the maker's quoters.
repeated string quoters = 3;
}
// Historical quote query. Same filter semantics as the stream, over a time
// window, newest first; scoped to the authenticated maker.
message ListRecentQuotesRequest {
reserved 1; // was maker_ids - the RPC is scoped to the authenticated maker
repeated uint32 spot_ids = 2;
repeated string quoters = 3;
// Inclusive lower/upper bounds on block time. `0`/absent = unbounded.
optional Timestamp start = 4;
optional Timestamp end = 5;
// Max rows to return. Server clamps to a sane ceiling; 0 = server default.
uint32 limit = 6;
}
message ListRecentQuotesResponse {
// Newest first.
repeated QuoteUpdate quotes = 1;
}
// Exact on-chain identity of a single quote update. All three fields are
// required to address a unique row: one transaction can carry several quote
// instructions, each touching several spots (legs).
message GetQuoteRequest {
string signature = 1;
uint32 instruction_index = 2;
uint32 leg_index = 3;
}
message GetQuoteResponse {
QuoteUpdate quote = 1;
}