Skip to main content

acta_maker_sdk/ws/maker/
quote.rs

1use crate::ws::managed::{
2    MakerWsEndpoint, ManagedMessageReceiver, ManagedWsConfig, ManagedWsError, ManagedWsEvent,
3    ManagedWsHandle, ManagedWsState, ReferenceChangeReceiver, SendTicket, WaitUntilReadyError,
4    spawn_managed_ws,
5};
6use crate::ws::types::{
7    AddChannelsData, AddMintsData, BatchQuotesMessage, CancelAllQuotesMessage, CancelQuoteData,
8    ClientMessage, IndicativePricesResponseMessage, QuoteMessage, RemoveChannelsData,
9    RemoveMintsData, ReplaceQuoteMessage, SubscribeData, UnsubscribeData,
10};
11use tokio::sync::broadcast;
12
13use super::MakerClientError;
14
15/// Maker quote-plane client.
16///
17/// `try_*` methods return immediately when the outbound queue is full. A
18/// successful [`SendTicket`] reports a socket write, not a server acknowledgement.
19#[derive(Clone)]
20pub struct MakerQuoteClient {
21    inner: ManagedWsHandle,
22}
23
24impl MakerQuoteClient {
25    pub fn spawn(config: ManagedWsConfig) -> Result<Self, MakerClientError> {
26        if config.endpoint != MakerWsEndpoint::Quote {
27            return Err(MakerClientError::WrongEndpoint {
28                client: "MakerQuoteClient",
29                required: MakerWsEndpoint::Quote,
30                actual: config.endpoint,
31            });
32        }
33        Ok(Self {
34            inner: spawn_managed_ws(config)?,
35        })
36    }
37
38    /// Send only in `connection_epoch`, the epoch whose recovery the strategy
39    /// applied; a reconnect in between rejects the quote with `NotReady`.
40    pub async fn quote_in_epoch(
41        &self,
42        payload: QuoteMessage,
43        connection_epoch: u64,
44    ) -> Result<(), ManagedWsError> {
45        self.inner
46            .send_in_epoch(ClientMessage::Quote(payload), connection_epoch)
47            .await
48    }
49
50    pub fn try_quote_in_epoch(
51        &self,
52        payload: QuoteMessage,
53        connection_epoch: u64,
54    ) -> Result<SendTicket, ManagedWsError> {
55        self.inner
56            .try_send_in_epoch(ClientMessage::Quote(payload), connection_epoch)
57    }
58
59    pub async fn replace_in_epoch(
60        &self,
61        payload: ReplaceQuoteMessage,
62        connection_epoch: u64,
63    ) -> Result<(), ManagedWsError> {
64        self.inner
65            .send_in_epoch(ClientMessage::ReplaceQuote(payload), connection_epoch)
66            .await
67    }
68
69    pub fn try_replace_in_epoch(
70        &self,
71        payload: ReplaceQuoteMessage,
72        connection_epoch: u64,
73    ) -> Result<SendTicket, ManagedWsError> {
74        self.inner
75            .try_send_in_epoch(ClientMessage::ReplaceQuote(payload), connection_epoch)
76    }
77
78    pub async fn batch_quotes_in_epoch(
79        &self,
80        payload: BatchQuotesMessage,
81        connection_epoch: u64,
82    ) -> Result<(), ManagedWsError> {
83        self.inner
84            .send_in_epoch(ClientMessage::BatchQuotes(payload), connection_epoch)
85            .await
86    }
87
88    pub fn try_batch_quotes_in_epoch(
89        &self,
90        payload: BatchQuotesMessage,
91        connection_epoch: u64,
92    ) -> Result<SendTicket, ManagedWsError> {
93        self.inner
94            .try_send_in_epoch(ClientMessage::BatchQuotes(payload), connection_epoch)
95    }
96
97    pub async fn cancel_quote(&self, payload: CancelQuoteData) -> Result<(), ManagedWsError> {
98        self.inner.ensure_ready()?;
99        self.inner.send(ClientMessage::CancelQuote(payload)).await
100    }
101
102    pub fn try_cancel_quote(&self, payload: CancelQuoteData) -> Result<SendTicket, ManagedWsError> {
103        self.inner.ensure_ready()?;
104        self.inner.try_send(ClientMessage::CancelQuote(payload))
105    }
106
107    pub async fn cancel_all_quotes(
108        &self,
109        payload: CancelAllQuotesMessage,
110    ) -> Result<(), ManagedWsError> {
111        self.inner.ensure_ready()?;
112        self.inner
113            .send(ClientMessage::CancelAllQuotes(payload))
114            .await
115    }
116
117    pub fn try_cancel_all_quotes(
118        &self,
119        payload: CancelAllQuotesMessage,
120    ) -> Result<SendTicket, ManagedWsError> {
121        self.inner.ensure_ready()?;
122        self.inner.try_send(ClientMessage::CancelAllQuotes(payload))
123    }
124
125    pub async fn indicative_prices_response(
126        &self,
127        payload: IndicativePricesResponseMessage,
128    ) -> Result<(), ManagedWsError> {
129        self.inner.ensure_ready()?;
130        self.inner
131            .send(ClientMessage::IndicativePricesResponse(payload))
132            .await
133    }
134
135    pub fn try_indicative_prices_response(
136        &self,
137        payload: IndicativePricesResponseMessage,
138    ) -> Result<SendTicket, ManagedWsError> {
139        self.inner.ensure_ready()?;
140        self.inner
141            .try_send(ClientMessage::IndicativePricesResponse(payload))
142    }
143
144    pub async fn subscribe(&self, payload: SubscribeData) -> Result<(), ManagedWsError> {
145        self.inner.ensure_ready()?;
146        self.inner.send(ClientMessage::Subscribe(payload)).await
147    }
148
149    pub async fn unsubscribe(&self, payload: UnsubscribeData) -> Result<(), ManagedWsError> {
150        self.inner.ensure_ready()?;
151        self.inner.send(ClientMessage::Unsubscribe(payload)).await
152    }
153
154    pub async fn add_mints(&self, payload: AddMintsData) -> Result<(), ManagedWsError> {
155        self.inner.ensure_ready()?;
156        self.inner.send(ClientMessage::AddMints(payload)).await
157    }
158
159    pub async fn remove_mints(&self, payload: RemoveMintsData) -> Result<(), ManagedWsError> {
160        self.inner.ensure_ready()?;
161        self.inner.send(ClientMessage::RemoveMints(payload)).await
162    }
163
164    pub async fn add_channels(&self, payload: AddChannelsData) -> Result<(), ManagedWsError> {
165        self.inner.ensure_ready()?;
166        self.inner.send(ClientMessage::AddChannels(payload)).await
167    }
168
169    pub async fn remove_channels(&self, payload: RemoveChannelsData) -> Result<(), ManagedWsError> {
170        self.inner.ensure_ready()?;
171        self.inner
172            .send(ClientMessage::RemoveChannels(payload))
173            .await
174    }
175
176    pub fn subscribe_messages(&self) -> ManagedMessageReceiver {
177        self.inner.subscribe_messages()
178    }
179
180    pub fn subscribe_reference_changes(&self) -> ReferenceChangeReceiver {
181        self.inner.subscribe_reference_changes()
182    }
183
184    pub fn subscribe_events(&self) -> broadcast::Receiver<ManagedWsEvent> {
185        self.inner.subscribe_events()
186    }
187
188    #[must_use]
189    pub fn state(&self) -> ManagedWsState {
190        self.inner.state()
191    }
192
193    pub async fn wait_until_ready(&self) -> Result<u64, WaitUntilReadyError> {
194        self.inner.wait_until_ready().await
195    }
196
197    #[must_use]
198    pub const fn raw(&self) -> &ManagedWsHandle {
199        &self.inner
200    }
201
202    #[must_use]
203    pub fn into_raw(self) -> ManagedWsHandle {
204        self.inner
205    }
206
207    pub async fn close(&self) -> Result<(), ManagedWsError> {
208        self.inner.close().await
209    }
210}
211
212#[cfg(test)]
213mod tests {
214    use std::sync::Arc;
215
216    use super::*;
217    use crate::ws::types::HelloData;
218
219    #[test]
220    fn quote_client_rejects_data_endpoint_before_spawning() {
221        let config = ManagedWsConfig::new(
222            "ws://localhost",
223            HelloData {
224                protocol_version: "1.0.0".to_string(),
225                features: Vec::new(),
226                client_name: None,
227                client_version: None,
228            },
229            Arc::new(crate::orders::BytesSigner::from_secret([1u8; 32])),
230            "owner",
231        )
232        .with_endpoint(MakerWsEndpoint::Data);
233
234        assert!(matches!(
235            MakerQuoteClient::spawn(config),
236            Err(MakerClientError::WrongEndpoint {
237                required: MakerWsEndpoint::Quote,
238                actual: MakerWsEndpoint::Data,
239                ..
240            })
241        ));
242    }
243}