Skip to main content

acta_maker_sdk/types/
messages.rs

1use std::time::SystemTime;
2
3use serde::{Deserialize, Serialize};
4use serde_with::{TimestampMilliSeconds, TimestampSeconds, serde_as};
5use strum::IntoStaticStr;
6use uuid::Uuid;
7
8use super::errors::ServerError;
9use super::ids::{
10    Balance, Decimals, MarketId, Nonce, OrderId, PositionType, Price, Quantity, QuoteCount,
11    RfqVersion, Strike, UserId,
12};
13
14#[derive(Debug, Clone, Serialize, Deserialize, IntoStaticStr)]
15#[serde(tag = "type", content = "data")]
16#[strum(serialize_all = "snake_case")]
17pub enum ServerMessage {
18    RfqCreated(RfqCreatedMessage),
19    RfqClosed(RfqClosedMessage),
20
21    QuoteAcknowledged(QuoteAcknowledgedMessage),
22    QuoteBestStatus(QuoteBestStatusMessage),
23    QuoteOutbid(QuoteOutbidMessage),
24    QuoteFilled(QuoteFilledMessage),
25    QuoteSelected(QuoteSelectedMessage),
26    QuoteCancelled(QuoteCancelledMessage),
27    QuoteRefreshRequested(QuoteRefreshRequestedMessage),
28    RfqAvailableAgain(RfqAvailableAgainMessage),
29
30    QuoteReceived(QuoteReceivedMessage),
31
32    ActiveRfqs(ActiveRfqsMessage),
33    MyQuotes(MyQuotesMessage),
34
35    OrderAccepted(OrderAcceptedMessage),
36    SponsoredTxToSign(SponsoredTxToSignMessage),
37    OrderSubmitted(OrderSubmittedMessage),
38    OrderConfirmed(OrderConfirmedMessage),
39    OrderFailed(OrderFailedMessage),
40
41    Pong(PongMessage),
42    Error(ServerError),
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct RfqOrderOption {
47    pub strike: Strike,
48}
49
50#[serde_as]
51#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct ActiveRfqInfo {
53    pub rfq_id: Uuid,
54    pub market: MarketId,
55    pub position_type: PositionType,
56    pub strike: Strike,
57    pub quantity: Quantity,
58    #[serde_as(as = "TimestampSeconds<i64>")]
59    pub expires_at: SystemTime,
60    pub quotes_count: QuoteCount,
61    pub best_price: Option<Price>,
62    #[serde(default, skip_serializing_if = "Vec::is_empty")]
63    pub order_options: Vec<RfqOrderOption>,
64}
65
66#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
67#[serde(rename_all = "lowercase")]
68pub enum QuoteStatus {
69    Pending,
70    Best,
71    Outbid,
72    Filled,
73    Expired,
74}
75
76#[serde_as]
77#[derive(Debug, Clone, Serialize, Deserialize)]
78pub struct MakerQuoteInfo {
79    pub rfq_id: Uuid,
80    pub order_id: OrderId,
81    pub market: MarketId,
82    pub strike: Strike,
83    pub price: Price,
84    pub quantity: Quantity,
85    #[serde_as(as = "TimestampSeconds<i64>")]
86    pub valid_until: SystemTime,
87    pub status: QuoteStatus,
88    #[serde_as(as = "TimestampSeconds<i64>")]
89    pub created_at: SystemTime,
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize)]
93pub struct MyQuotesMessage {
94    pub quotes: Vec<MakerQuoteInfo>,
95}
96
97#[serde_as]
98#[derive(Debug, Clone, Serialize, Deserialize)]
99pub struct RfqCreatedMessage {
100    pub rfq_id: Uuid,
101    #[serde(default)]
102    pub rfq_version: RfqVersion,
103    #[serde(default, skip_serializing_if = "Option::is_none")]
104    pub client_request_id: Option<Uuid>,
105    #[serde_as(as = "TimestampSeconds<i64>")]
106    pub expires_at: SystemTime,
107    #[serde_as(as = "TimestampSeconds<i64>")]
108    pub created_at: SystemTime,
109    #[serde(default, skip_serializing_if = "Vec::is_empty")]
110    pub order_options: Vec<RfqOrderOption>,
111}
112
113#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, IntoStaticStr)]
114#[serde(rename_all = "snake_case")]
115#[strum(serialize_all = "snake_case")]
116pub enum RfqCloseReason {
117    Expired,
118    TakerCancelled,
119    Filled,
120    MarketExpired,
121    LadderTimeout,
122}
123
124#[serde_as]
125#[derive(Debug, Clone, Serialize, Deserialize)]
126pub struct RfqClosedMessage {
127    pub rfq_id: Uuid,
128    #[serde(default)]
129    pub rfq_version: RfqVersion,
130    pub reason: RfqCloseReason,
131    #[serde(default, skip_serializing_if = "Option::is_none")]
132    pub your_quote: Option<RfqClosedYourQuote>,
133    #[serde(default, skip_serializing_if = "Option::is_none")]
134    pub winner: Option<RfqClosedWinner>,
135    #[serde_as(as = "TimestampSeconds<i64>")]
136    pub closed_at: SystemTime,
137}
138
139#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, IntoStaticStr)]
140#[serde(rename_all = "snake_case")]
141#[strum(serialize_all = "snake_case")]
142pub enum QuoteFinalStatus {
143    Expired,
144    Outbid,
145    Cancelled,
146    Filled,
147}
148
149#[derive(Debug, Clone, Serialize, Deserialize)]
150pub struct RfqClosedYourQuote {
151    pub order_id: OrderId,
152    pub status: QuoteFinalStatus,
153    pub price: Price,
154}
155
156#[derive(Debug, Clone, Serialize, Deserialize)]
157pub struct RfqClosedWinner {
158    pub maker: UserId,
159    pub price: Price,
160    #[serde(default, skip_serializing_if = "Option::is_none")]
161    pub tx_signature: Option<String>,
162}
163
164#[derive(Debug, Clone, Serialize, Deserialize)]
165pub struct QuoteAcknowledgedMessage {
166    pub rfq_id: Uuid,
167    pub order_id: OrderId,
168    #[serde(default, skip_serializing_if = "Option::is_none")]
169    pub replaced_order_id: Option<OrderId>,
170}
171
172#[derive(Debug, Clone, Serialize, Deserialize)]
173pub struct QuoteBestStatusMessage {
174    pub rfq_id: Uuid,
175    pub order_id: OrderId,
176    pub is_best: bool,
177    #[serde(default, skip_serializing_if = "Option::is_none")]
178    pub current_best_price: Option<Price>,
179}
180
181#[derive(Debug, Clone, Serialize, Deserialize)]
182pub struct QuoteOutbidMessage {
183    pub rfq_id: Uuid,
184    pub order_id: OrderId,
185    pub your_price: Price,
186    #[serde(default, skip_serializing_if = "Option::is_none")]
187    pub current_best_price: Option<Price>,
188}
189
190#[serde_as]
191#[derive(Debug, Clone, Serialize, Deserialize)]
192pub struct QuoteFilledMessage {
193    pub rfq_id: Uuid,
194    pub order_id: OrderId,
195    pub taker: UserId,
196    pub price: Price,
197    pub quantity: Quantity,
198    pub strike: Strike,
199    pub position_pda: String,
200    pub tx_signature: String,
201    #[serde_as(as = "TimestampSeconds<i64>")]
202    pub filled_at: SystemTime,
203}
204
205#[serde_as]
206#[derive(Debug, Clone, Serialize, Deserialize)]
207pub struct QuoteReceivedMessage {
208    pub rfq_id: Uuid,
209    pub strike: Strike,
210    pub maker: UserId,
211    pub price: Price,
212    #[serde_as(as = "TimestampSeconds<i64>")]
213    pub valid_until: SystemTime,
214    pub nonce: Nonce,
215    pub order_id: OrderId,
216}
217
218#[serde_as]
219#[derive(Debug, Clone, Serialize, Deserialize)]
220pub struct QuoteRefreshRequestedMessage {
221    pub rfq_id: Uuid,
222    pub strike: Strike,
223    #[serde_as(as = "TimestampSeconds<i64>")]
224    pub min_valid_until: SystemTime,
225    pub reason: String,
226}
227
228#[serde_as]
229#[derive(Debug, Clone, Serialize, Deserialize)]
230pub struct QuoteSelectedMessage {
231    pub rfq_id: Uuid,
232    pub order_id: OrderId,
233    pub taker: UserId,
234    pub price: Price,
235    pub quantity: Quantity,
236    pub strike: Strike,
237    #[serde_as(as = "TimestampSeconds<i64>")]
238    pub signature_deadline: SystemTime,
239}
240
241#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, IntoStaticStr)]
242#[serde(rename_all = "snake_case")]
243#[strum(serialize_all = "snake_case")]
244pub enum QuoteCancelReason {
245    Requested,
246    RiskCheck,
247    RfqAccepted,
248}
249
250#[serde_as]
251#[derive(Debug, Clone, Serialize, Deserialize)]
252pub struct QuoteCancelledMessage {
253    pub rfq_id: Uuid,
254    #[serde(default)]
255    pub order_ids: Vec<OrderId>,
256    pub reason: QuoteCancelReason,
257    #[serde_as(as = "TimestampSeconds<i64>")]
258    pub cancelled_at: SystemTime,
259}
260
261#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, IntoStaticStr)]
262#[serde(rename_all = "snake_case")]
263#[strum(serialize_all = "snake_case")]
264pub enum RfqAvailableAgainReason {
265    SignatureTimeout,
266    TxFailed,
267    TxBuildFailed,
268}
269
270impl std::fmt::Display for RfqAvailableAgainReason {
271    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
272        f.write_str(self.into())
273    }
274}
275
276#[serde_as]
277#[derive(Debug, Clone, Serialize, Deserialize)]
278pub struct RfqAvailableAgainMessage {
279    pub rfq_id: Uuid,
280    #[serde(default)]
281    pub rfq_version: RfqVersion,
282    pub reason: RfqAvailableAgainReason,
283    #[serde_as(as = "TimestampSeconds<i64>")]
284    pub available_again_at: SystemTime,
285}
286
287#[derive(Debug, Clone, Serialize, Deserialize)]
288pub struct ActiveRfqsMessage {
289    pub rfqs: Vec<ActiveRfqInfo>,
290}
291
292#[derive(Debug, Clone, Serialize, Deserialize)]
293pub struct OrderAcceptedMessage {
294    pub order_id: OrderId,
295}
296
297#[serde_as]
298#[derive(Debug, Clone, Serialize, Deserialize)]
299pub struct SponsoredTxToSignMessage {
300    pub order_id: OrderId,
301    pub tx_base64: String,
302    #[serde_as(as = "TimestampSeconds<i64>")]
303    pub signature_deadline: SystemTime,
304}
305
306#[derive(Debug, Clone, Serialize, Deserialize)]
307pub struct OrderSubmittedMessage {
308    pub order_id: OrderId,
309    pub tx_signature: String,
310}
311
312#[derive(Debug, Clone, Serialize, Deserialize)]
313pub struct OrderConfirmedMessage {
314    pub order_id: OrderId,
315    pub position_pda: String,
316    #[serde(default, skip_serializing_if = "Option::is_none")]
317    pub tx_signature: Option<String>,
318}
319
320#[derive(Debug, Clone, Serialize, Deserialize)]
321pub struct OrderFailedMessage {
322    pub order_id: OrderId,
323    pub reason: String,
324}
325
326#[serde_as]
327#[derive(Debug, Clone, Serialize, Deserialize)]
328pub struct PongMessage {
329    #[serde_as(as = "TimestampMilliSeconds<i64>")]
330    pub server_time_unix_ms: SystemTime,
331}
332
333// =============================================================================
334// Caps
335// =============================================================================
336
337/// Open interest cap status for a single underlying token.
338#[derive(Debug, Clone, Serialize, Deserialize)]
339pub struct TokenCapInfo {
340    pub underlying_mint: String,
341    pub symbol: String,
342    pub current_oi: Quantity,
343    pub max_oi: Quantity,
344    pub utilization: f64,
345}
346
347/// Open interest cap status for a single market.
348#[derive(Debug, Clone, Serialize, Deserialize)]
349pub struct MarketCapInfo {
350    pub market_id: MarketId,
351    pub current_oi: Quantity,
352    pub max_oi: Quantity,
353    pub utilization: f64,
354}
355
356/// Maker's personal position cap status.
357#[derive(Debug, Clone, Serialize, Deserialize)]
358pub struct MakerPositionCapInfo {
359    pub current: u32,
360    pub limit: u32,
361}
362
363/// Maker's notional exposure cap per underlying.
364#[derive(Debug, Clone, Serialize, Deserialize)]
365pub struct MakerNotionalCapInfo {
366    pub underlying_mint: String,
367    pub symbol: String,
368    pub current: Quantity,
369    pub limit: Quantity,
370}
371
372/// Maker's available balance for quoting.
373#[derive(Debug, Clone, Serialize, Deserialize)]
374pub struct MakerBalanceCapInfo {
375    pub mint: String,
376    pub symbol: String,
377    pub decimals: Decimals,
378    pub deposited: Balance,
379    pub committed: Balance,
380    pub available: Balance,
381}
382
383/// Quote-level notional cap status for a single quote mint.
384#[derive(Debug, Clone, Serialize, Deserialize)]
385pub struct QuoteCapInfo {
386    pub quote_mint: String,
387    pub symbol: String,
388    /// Current total premium committed for this quote mint.
389    pub current_notional: Balance,
390    /// Maximum allowed notional (budget). `0` means unlimited.
391    pub max_notional: Balance,
392    pub utilization: f64,
393}