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