Proto schema
The canonical schema for sweetspot.api.v1. Synced from the sweetspot-protos submodule; regenerate SDKs and docs after bumping that submodule.
- Download bundled
api.proto— every service insweetspot.api.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 sweetspot.api.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);
// 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 all responses.
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);
// 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);
}
// -- 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);
}
// -- 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);
}
// -- 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.
// - `GetTrades.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 GetTrades(GetTradesRequest) returns (GetTradesResponse);
rpc GetCandles(GetCandlesRequest) returns (GetCandlesResponse);
}
// -- 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 sweetspot.api.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 {
SpotId base = 1;
SpotId quote = 2;
}
// -- Solana-level primitives -----------------------------------------
message Pubkey {
bytes key = 1;
}
message Signature {
bytes signature = 1;
}
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;
Pubkey mint = 3;
Pubkey program_id = 4;
uint32 decimals = 5;
uint32 atoms_per_lots = 6;
}
message SpotAssets {
repeated SpotMetadata assets = 1;
}
message MakerMarketState {
SpotId spot_id = 1;
uint64 order_sequence = 2;
}
// Low-level per-spot balance sample, used by the indexer and by historical
// queries. `MakerBalanceEvent` is the streaming form.
message BalanceUpdate {
SpotId spot_id = 1;
MakerId maker_id = 2;
// Token units (e.g. "1.5" SOL), not raw atoms.
Decimal balance = 3;
uint64 slot = 4;
}
// -- 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 {
// The pubkey (quoting authority) that will sign the challenge.
Pubkey 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 {
Pubkey pubkey = 1;
// Signature over AUTH_DOMAIN_PREFIX || nonce from the outstanding Challenge.
Signature signature = 2;
}
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;
}
// -- 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;
}
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;
}
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;
}
// -- 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 ------------------------------------------
// Per-pair volume + fill count over a single rolling window.
// `volume_quote = sum(size * price)` in the pair's quote asset.
message WindowStats {
StatsWindow window = 1;
Decimal volume_base = 2;
Decimal volume_quote = 3;
uint64 fill_count = 4;
}
// Cross-venue equivalent of `WindowStats`. `volume_base` is dropped - summing
// base across pairs with different base assets mixes denominations.
message CrossVenueWindowStats {
StatsWindow window = 1;
Decimal volume_quote = 2;
uint64 fill_count = 3;
}
// 1h / 24h / 30d bundled together. All three are always populated even
// if a pair has no fills in the smaller windows (zero is meaningful).
message PairVolume {
Pair pair = 1;
WindowStats one_hour = 2;
WindowStats twenty_four_hour = 3;
WindowStats thirty_day = 4;
// Most recent landed fill price. Absent if no fills observed yet.
Decimal last_price = 5;
}
// -- GetSummary ------------------------------------------------------
message GetSummaryRequest {}
message GetSummaryResponse {
// How many pairs the indexer has discovered and is streaming books for.
uint32 active_pairs = 1;
// How many makers are registered on-chain.
uint32 active_makers = 2;
// Cross-venue aggregates over each window - quote volume + fill count
// totalled across every pair.
CrossVenueWindowStats one_hour = 3;
CrossVenueWindowStats twenty_four_hour = 4;
CrossVenueWindowStats thirty_day = 5;
// All-time totals, computed from the events table since the archiver
// started recording.
Decimal volume_quote_all_time = 7;
uint64 fill_count_all_time = 8;
// Distinct taker accounts seen in the last 24h. A rough proxy for active
// users.
uint32 unique_traders_24h = 9;
// Largest single USDC-quoted trade in the last 24h by notional value
// (`size * price`). Absent if no USDC-quoted fills have happened.
LargestTrade largest_trade_24h = 10;
// Cross-venue order-update rate - every observed change to a pair's
// virtual book (maker cancel/replace plus oracle moves).
UpdateRate update_rate = 11;
// Cross-venue oracle-update rate - counts observed oracle fair changes
// across every spot market.
UpdateRate oracle_update_rate = 12;
}
message LargestTrade {
Pair pair = 1;
Decimal size = 2;
Decimal price = 3;
Timestamp ts = 4;
// `size * price`, in USDC for summary responses.
Decimal notional = 5;
}
// -- GetVolumeBreakdown ----------------------------------------------
message GetVolumeBreakdownRequest {
// Optional pair filter. Empty = every pair the venue has fills for in
// the largest window (30d).
repeated Pair pairs = 1;
}
message GetVolumeBreakdownResponse {
// Ordered by 24h volume descending.
repeated PairVolume pairs = 1;
}
// -- GetVolumeSeries -------------------------------------------------
message GetVolumeSeriesRequest {
// Optional pair filter. Absent = cross-venue aggregate.
Pair pair = 1;
// Window selects both the time horizon and the bucket size:
// 1H -> 60 x 1m buckets
// 24H -> 288 x 5m buckets
// 30D -> 720 x 1h buckets
StatsWindow window = 2;
}
message VolumeBucket {
// Bucket start (inclusive).
Timestamp ts = 1;
Decimal volume_base = 2;
Decimal volume_quote = 3;
uint64 fill_count = 4;
}
message GetVolumeSeriesResponse {
// Ordered oldest-first.
repeated VolumeBucket buckets = 1;
// Width of each bucket in seconds - clients can label the X axis
// without having to repeat the server's window->interval mapping.
uint32 bucket_seconds = 2;
}
// -- GetAssetTvls ----------------------------------------------------
message GetAssetTvlsRequest {}
message AssetTvl {
SpotMetadata asset = 1;
// Sum of every maker's balance for this spot, in token units (e.g.
// "1500.5" SOL - not atoms).
Decimal total_balance = 2;
// How many makers currently hold a non-zero balance of this asset.
uint32 maker_count = 3;
}
message GetAssetTvlsResponse {
repeated AssetTvl assets = 1;
}
// -- UpdateRate ------------------------------------------------------
// Rolling event rate. Used for both order updates and oracle updates on
// `GetSummaryResponse`.
message UpdateRate {
// Updates within the trailing 1 second.
double per_second_1s = 1;
// Average updates / second over the trailing 1 minute.
double per_second_1m = 2;
// Average updates / second over the trailing 5 minutes.
double per_second_5m = 3;
}
// -- 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;
}
// 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 {
// 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::Confirmed).
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;
}
// -- 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;
}
message SlotEvent {
uint64 slot = 1;
Timestamp ts = 2;
}
// -- Per-tx lifecycle ------------------------------------------------
// Server has accepted the tx for submission (pre-confirmation).
message TxAckEvent {
Signature signature = 1;
Timestamp ts = 2;
}
// Tx landed and did not error.
message TxConfirmedEvent {
Signature signature = 1;
uint64 slot = 2;
Timestamp ts = 3;
}
// Tx landed but reverted, or timed out before landing.
message TxFailedEvent {
Signature signature = 1;
string reason = 2;
Timestamp ts = 3;
}
// Multiplexed tx-status stream envelope.
message TxStatusEvent {
oneof kind {
TxAckEvent ack = 1;
TxConfirmedEvent confirmed = 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/confirmed/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;
}
message SubmitTxResponse {
// Parsed signature from the submitted tx. Use this to correlate with
// subsequent TxAckEvent / TxConfirmedEvent / TxFailedEvent received
// on `SubscribeTxStatus`.
Signature signature = 1;
// Server wall-clock timestamp the submission was accepted.
Timestamp ts = 2;
}
// 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 {
repeated Pubkey payers = 1;
}
message SubscribeBlockhashRequest {}
message SubscribeSlotsRequest {}
// 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 {}
// -- 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;
}
// Historical trade row. Same shape as the live `FillEvent`.
message Trade {
Pair pair = 1;
Side side = 2;
Decimal price = 3;
Decimal size = 4;
uint64 slot = 5;
Timestamp ts = 6;
}
// -- GetTrades -------------------------------------------------------
message GetTradesRequest {
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 GetTradesResponse {
// Ordered newest-first.
repeated Trade trades = 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;
}