Skip to main content

nautilus_common/messages/defi/
mod.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! DeFi (Decentralized Finance) specific messages.
17
18use std::any::Any;
19
20use nautilus_core::{UUID4, UnixNanos};
21use nautilus_model::{
22    defi::Blockchain,
23    identifiers::{ClientId, Venue},
24};
25use serde::{Deserialize, Serialize};
26
27pub mod request;
28pub mod subscribe;
29pub mod unsubscribe;
30
31// Re-exports
32pub use request::RequestPoolSnapshot;
33pub use subscribe::{
34    SubscribeBlocks, SubscribePool, SubscribePoolFeeCollects, SubscribePoolFlashEvents,
35    SubscribePoolLiquidityUpdates, SubscribePoolSwaps,
36};
37pub use unsubscribe::{
38    UnsubscribeBlocks, UnsubscribePool, UnsubscribePoolFeeCollects, UnsubscribePoolFlashEvents,
39    UnsubscribePoolLiquidityUpdates, UnsubscribePoolSwaps,
40};
41
42#[derive(Clone, Debug, Serialize, Deserialize)]
43pub enum DefiDataCommand {
44    Request(DefiRequestCommand),
45    Subscribe(DefiSubscribeCommand),
46    Unsubscribe(DefiUnsubscribeCommand),
47}
48
49impl PartialEq for DefiDataCommand {
50    fn eq(&self, other: &Self) -> bool {
51        self.command_id() == other.command_id()
52    }
53}
54
55impl DefiDataCommand {
56    /// Converts the command to a dyn Any trait object for messaging.
57    pub fn as_any(&self) -> &dyn Any {
58        self
59    }
60
61    pub fn command_id(&self) -> UUID4 {
62        match self {
63            Self::Request(cmd) => *cmd.request_id(),
64            Self::Subscribe(cmd) => cmd.command_id(),
65            Self::Unsubscribe(cmd) => cmd.command_id(),
66        }
67    }
68
69    pub fn client_id(&self) -> Option<&ClientId> {
70        match self {
71            Self::Request(cmd) => cmd.client_id(),
72            Self::Subscribe(cmd) => cmd.client_id(),
73            Self::Unsubscribe(cmd) => cmd.client_id(),
74        }
75    }
76
77    pub fn venue(&self) -> Option<&Venue> {
78        match self {
79            Self::Request(cmd) => cmd.venue(),
80            Self::Subscribe(cmd) => cmd.venue(),
81            Self::Unsubscribe(cmd) => cmd.venue(),
82        }
83    }
84
85    pub fn ts_init(&self) -> UnixNanos {
86        match self {
87            Self::Request(cmd) => cmd.ts_init(),
88            Self::Subscribe(cmd) => cmd.ts_init(),
89            Self::Unsubscribe(cmd) => cmd.ts_init(),
90        }
91    }
92}
93
94#[derive(Clone, Debug, Serialize, Deserialize)]
95pub enum DefiSubscribeCommand {
96    Blocks(SubscribeBlocks),
97    Pool(SubscribePool),
98    PoolSwaps(SubscribePoolSwaps),
99    PoolLiquidityUpdates(SubscribePoolLiquidityUpdates),
100    PoolFeeCollects(SubscribePoolFeeCollects),
101    PoolFlashEvents(SubscribePoolFlashEvents),
102}
103
104impl PartialEq for DefiSubscribeCommand {
105    fn eq(&self, other: &Self) -> bool {
106        self.command_id() == other.command_id()
107    }
108}
109
110impl DefiSubscribeCommand {
111    /// Converts the command to a dyn Any trait object for messaging.
112    pub fn as_any(&self) -> &dyn Any {
113        self
114    }
115
116    /// Converts this subscribe command into its matching unsubscribe command.
117    ///
118    /// Preserves the subscribed data identity, client route, and parameters while replacing the
119    /// command ID and initialization timestamp with the supplied values.
120    #[must_use]
121    pub fn into_unsubscribe(self, command_id: UUID4, ts_init: UnixNanos) -> DefiUnsubscribeCommand {
122        match self {
123            Self::Blocks(cmd) => DefiUnsubscribeCommand::Blocks(UnsubscribeBlocks::new(
124                cmd.chain,
125                cmd.client_id,
126                command_id,
127                ts_init,
128                cmd.params,
129            )),
130            Self::Pool(cmd) => DefiUnsubscribeCommand::Pool(UnsubscribePool::new(
131                cmd.instrument_id,
132                cmd.client_id,
133                command_id,
134                ts_init,
135                cmd.params,
136            )),
137            Self::PoolSwaps(cmd) => DefiUnsubscribeCommand::PoolSwaps(UnsubscribePoolSwaps::new(
138                cmd.instrument_id,
139                cmd.client_id,
140                command_id,
141                ts_init,
142                cmd.params,
143            )),
144            Self::PoolLiquidityUpdates(cmd) => {
145                DefiUnsubscribeCommand::PoolLiquidityUpdates(UnsubscribePoolLiquidityUpdates::new(
146                    cmd.instrument_id,
147                    cmd.client_id,
148                    command_id,
149                    ts_init,
150                    cmd.params,
151                ))
152            }
153            Self::PoolFeeCollects(cmd) => {
154                DefiUnsubscribeCommand::PoolFeeCollects(UnsubscribePoolFeeCollects::new(
155                    cmd.instrument_id,
156                    cmd.client_id,
157                    command_id,
158                    ts_init,
159                    cmd.params,
160                ))
161            }
162            Self::PoolFlashEvents(cmd) => {
163                DefiUnsubscribeCommand::PoolFlashEvents(UnsubscribePoolFlashEvents::new(
164                    cmd.instrument_id,
165                    cmd.client_id,
166                    command_id,
167                    ts_init,
168                    cmd.params,
169                ))
170            }
171        }
172    }
173
174    /// Returns the blockchain associated with this command.
175    ///
176    /// # Panics
177    ///
178    /// Panics if the instrument ID's venue cannot be parsed as a valid blockchain venue
179    /// for `Pool`, `PoolSwaps`, `PoolLiquidityUpdates`, `PoolFeeCollects`, or `PoolFlashEvents` commands.
180    pub fn blockchain(&self) -> Blockchain {
181        match self {
182            Self::Blocks(cmd) => cmd.chain,
183            Self::Pool(cmd) => cmd.instrument_id.blockchain().expect("Invalid venue"),
184            Self::PoolSwaps(cmd) => cmd.instrument_id.blockchain().expect("Invalid venue"),
185            Self::PoolLiquidityUpdates(cmd) => {
186                cmd.instrument_id.blockchain().expect("Invalid venue")
187            }
188            Self::PoolFeeCollects(cmd) => cmd.instrument_id.blockchain().expect("Invalid venue"),
189            Self::PoolFlashEvents(cmd) => cmd.instrument_id.blockchain().expect("Invalid venue"),
190        }
191    }
192
193    pub fn command_id(&self) -> UUID4 {
194        match self {
195            Self::Blocks(cmd) => cmd.command_id,
196            Self::Pool(cmd) => cmd.command_id,
197            Self::PoolSwaps(cmd) => cmd.command_id,
198            Self::PoolLiquidityUpdates(cmd) => cmd.command_id,
199            Self::PoolFeeCollects(cmd) => cmd.command_id,
200            Self::PoolFlashEvents(cmd) => cmd.command_id,
201        }
202    }
203
204    pub fn client_id(&self) -> Option<&ClientId> {
205        match self {
206            Self::Blocks(cmd) => cmd.client_id.as_ref(),
207            Self::Pool(cmd) => cmd.client_id.as_ref(),
208            Self::PoolSwaps(cmd) => cmd.client_id.as_ref(),
209            Self::PoolLiquidityUpdates(cmd) => cmd.client_id.as_ref(),
210            Self::PoolFeeCollects(cmd) => cmd.client_id.as_ref(),
211            Self::PoolFlashEvents(cmd) => cmd.client_id.as_ref(),
212        }
213    }
214
215    // TODO: TBD
216    pub fn venue(&self) -> Option<&Venue> {
217        match self {
218            Self::Blocks(_) => None,
219            Self::Pool(_) => None,
220            Self::PoolSwaps(_) => None,
221            Self::PoolLiquidityUpdates(_) => None,
222            Self::PoolFeeCollects(_) => None,
223            Self::PoolFlashEvents(_) => None,
224        }
225    }
226
227    pub fn ts_init(&self) -> UnixNanos {
228        match self {
229            Self::Blocks(cmd) => cmd.ts_init,
230            Self::PoolSwaps(cmd) => cmd.ts_init,
231            Self::PoolLiquidityUpdates(cmd) => cmd.ts_init,
232            Self::Pool(cmd) => cmd.ts_init,
233            Self::PoolFeeCollects(cmd) => cmd.ts_init,
234            Self::PoolFlashEvents(cmd) => cmd.ts_init,
235        }
236    }
237}
238
239#[derive(Clone, Debug, Serialize, Deserialize)]
240pub enum DefiUnsubscribeCommand {
241    Blocks(UnsubscribeBlocks),
242    Pool(UnsubscribePool),
243    PoolSwaps(UnsubscribePoolSwaps),
244    PoolLiquidityUpdates(UnsubscribePoolLiquidityUpdates),
245    PoolFeeCollects(UnsubscribePoolFeeCollects),
246    PoolFlashEvents(UnsubscribePoolFlashEvents),
247}
248
249impl PartialEq for DefiUnsubscribeCommand {
250    fn eq(&self, other: &Self) -> bool {
251        self.command_id() == other.command_id()
252    }
253}
254
255impl DefiUnsubscribeCommand {
256    /// Converts the command to a dyn Any trait object for messaging.
257    pub fn as_any(&self) -> &dyn Any {
258        self
259    }
260
261    /// Returns the blockchain associated with this command.
262    ///
263    /// # Panics
264    ///
265    /// Panics if the instrument ID's venue cannot be parsed as a valid blockchain venue
266    /// for `Pool`, `PoolSwaps`, `PoolLiquidityUpdates`, `PoolFeeCollects`, or `PoolFlashEvents` commands.
267    pub fn blockchain(&self) -> Blockchain {
268        match self {
269            Self::Blocks(cmd) => cmd.chain,
270            Self::Pool(cmd) => cmd.instrument_id.blockchain().expect("Invalid venue"),
271            Self::PoolSwaps(cmd) => cmd.instrument_id.blockchain().expect("Invalid venue"),
272            Self::PoolLiquidityUpdates(cmd) => {
273                cmd.instrument_id.blockchain().expect("Invalid venue")
274            }
275            Self::PoolFeeCollects(cmd) => cmd.instrument_id.blockchain().expect("Invalid venue"),
276            Self::PoolFlashEvents(cmd) => cmd.instrument_id.blockchain().expect("Invalid venue"),
277        }
278    }
279
280    pub fn command_id(&self) -> UUID4 {
281        match self {
282            Self::Blocks(cmd) => cmd.command_id,
283            Self::Pool(cmd) => cmd.command_id,
284            Self::PoolSwaps(cmd) => cmd.command_id,
285            Self::PoolLiquidityUpdates(cmd) => cmd.command_id,
286            Self::PoolFeeCollects(cmd) => cmd.command_id,
287            Self::PoolFlashEvents(cmd) => cmd.command_id,
288        }
289    }
290
291    pub fn client_id(&self) -> Option<&ClientId> {
292        match self {
293            Self::Blocks(cmd) => cmd.client_id.as_ref(),
294            Self::Pool(cmd) => cmd.client_id.as_ref(),
295            Self::PoolSwaps(cmd) => cmd.client_id.as_ref(),
296            Self::PoolLiquidityUpdates(cmd) => cmd.client_id.as_ref(),
297            Self::PoolFeeCollects(cmd) => cmd.client_id.as_ref(),
298            Self::PoolFlashEvents(cmd) => cmd.client_id.as_ref(),
299        }
300    }
301
302    // TODO: TBD
303    pub fn venue(&self) -> Option<&Venue> {
304        match self {
305            Self::Blocks(_) => None,
306            Self::Pool(_) => None,
307            Self::PoolSwaps(_) => None,
308            Self::PoolLiquidityUpdates(_) => None,
309            Self::PoolFeeCollects(_) => None,
310            Self::PoolFlashEvents(_) => None,
311        }
312    }
313
314    pub fn ts_init(&self) -> UnixNanos {
315        match self {
316            Self::Blocks(cmd) => cmd.ts_init,
317            Self::Pool(cmd) => cmd.ts_init,
318            Self::PoolSwaps(cmd) => cmd.ts_init,
319            Self::PoolLiquidityUpdates(cmd) => cmd.ts_init,
320            Self::PoolFeeCollects(cmd) => cmd.ts_init,
321            Self::PoolFlashEvents(cmd) => cmd.ts_init,
322        }
323    }
324}
325
326#[derive(Clone, Debug, Serialize, Deserialize)]
327pub enum DefiRequestCommand {
328    PoolSnapshot(RequestPoolSnapshot),
329}
330
331impl PartialEq for DefiRequestCommand {
332    fn eq(&self, other: &Self) -> bool {
333        self.request_id() == other.request_id()
334    }
335}
336
337impl DefiRequestCommand {
338    /// Converts the command to a dyn Any trait object for messaging.
339    pub fn as_any(&self) -> &dyn Any {
340        self
341    }
342
343    pub fn request_id(&self) -> &UUID4 {
344        match self {
345            Self::PoolSnapshot(cmd) => &cmd.request_id,
346        }
347    }
348
349    pub fn client_id(&self) -> Option<&ClientId> {
350        match self {
351            Self::PoolSnapshot(cmd) => cmd.client_id.as_ref(),
352        }
353    }
354
355    pub fn venue(&self) -> Option<&Venue> {
356        match self {
357            Self::PoolSnapshot(cmd) => Some(&cmd.instrument_id.venue),
358        }
359    }
360
361    pub fn ts_init(&self) -> UnixNanos {
362        match self {
363            Self::PoolSnapshot(cmd) => cmd.ts_init,
364        }
365    }
366}