nautilus_blockchain/exchanges/
extended.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 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
16use std::{ops::Deref, sync::Arc};
17
18use hypersync_client::simple_types::Log;
19use nautilus_model::{
20    defi::{
21        dex::{Dex, SharedDex},
22        token::Token,
23    },
24    enums::OrderSide,
25    types::{Price, Quantity},
26};
27
28use crate::events::{
29    burn::BurnEvent, collect::CollectEvent, flash::FlashEvent, initialize::InitializeEvent,
30    mint::MintEvent, pool_created::PoolCreatedEvent, swap::SwapEvent,
31};
32
33type ConvertTradeDataFn =
34    fn(&Token, &Token, &SwapEvent) -> anyhow::Result<(OrderSide, Quantity, Price)>;
35
36/// Extended DEX wrapper that adds provider-specific event parsing capabilities to the domain `Dex` model.
37#[derive(Debug, Clone)]
38pub struct DexExtended {
39    /// The core domain Dex object being extended.
40    pub dex: SharedDex,
41    /// Function to parse pool creation events.
42    pub parse_pool_created_event_fn: Option<fn(Log) -> anyhow::Result<PoolCreatedEvent>>,
43    /// Function to parse initialize events.
44    pub parse_initialize_event_fn: Option<fn(SharedDex, Log) -> anyhow::Result<InitializeEvent>>,
45    /// Function to parse swap events.
46    pub parse_swap_event_fn: Option<fn(SharedDex, Log) -> anyhow::Result<SwapEvent>>,
47    /// Function to parse mint events.
48    pub parse_mint_event_fn: Option<fn(SharedDex, Log) -> anyhow::Result<MintEvent>>,
49    /// Function to parse burn events.
50    pub parse_burn_event_fn: Option<fn(SharedDex, Log) -> anyhow::Result<BurnEvent>>,
51    /// Function to parse collect events.
52    pub parse_collect_event_fn: Option<fn(SharedDex, Log) -> anyhow::Result<CollectEvent>>,
53    /// Function to parse flash events.
54    pub parse_flash_event_fn: Option<fn(SharedDex, Log) -> anyhow::Result<FlashEvent>>,
55    /// Function to convert to trade data.
56    pub convert_to_trade_data_fn: Option<ConvertTradeDataFn>,
57}
58
59impl DexExtended {
60    /// Creates a new [`DexExtended`] wrapper around a domain `Dex` object.
61    #[must_use]
62    pub fn new(dex: Dex) -> Self {
63        Self {
64            dex: Arc::new(dex),
65            parse_pool_created_event_fn: None,
66            parse_initialize_event_fn: None,
67            parse_swap_event_fn: None,
68            parse_mint_event_fn: None,
69            parse_burn_event_fn: None,
70            parse_collect_event_fn: None,
71            convert_to_trade_data_fn: None,
72            parse_flash_event_fn: None,
73        }
74    }
75
76    /// Sets the function used to parse pool creation events for this Dex.
77    pub fn set_pool_created_event_parsing(
78        &mut self,
79        parse_pool_created_event: fn(Log) -> anyhow::Result<PoolCreatedEvent>,
80    ) {
81        self.parse_pool_created_event_fn = Some(parse_pool_created_event);
82    }
83
84    /// Sets the function used to parse initialize events for this Dex.
85    pub fn set_initialize_event_parsing(
86        &mut self,
87        parse_initialize_event: fn(SharedDex, Log) -> anyhow::Result<InitializeEvent>,
88    ) {
89        self.parse_initialize_event_fn = Some(parse_initialize_event);
90    }
91
92    /// Sets the function used to parse swap events for this Dex.
93    pub fn set_swap_event_parsing(
94        &mut self,
95        parse_swap_event: fn(SharedDex, Log) -> anyhow::Result<SwapEvent>,
96    ) {
97        self.parse_swap_event_fn = Some(parse_swap_event);
98    }
99
100    /// Sets the function used to parse mint events for this Dex.
101    pub fn set_mint_event_parsing(
102        &mut self,
103        parse_mint_event: fn(SharedDex, Log) -> anyhow::Result<MintEvent>,
104    ) {
105        self.parse_mint_event_fn = Some(parse_mint_event);
106    }
107
108    /// Sets the function used to parse burn events for this Dex.
109    pub fn set_burn_event_parsing(
110        &mut self,
111        parse_burn_event: fn(SharedDex, Log) -> anyhow::Result<BurnEvent>,
112    ) {
113        self.parse_burn_event_fn = Some(parse_burn_event);
114    }
115
116    /// Sets the function used to parse collect events for this Dex.
117    pub fn set_collect_event_parsing(
118        &mut self,
119        parse_collect_event: fn(SharedDex, Log) -> anyhow::Result<CollectEvent>,
120    ) {
121        self.parse_collect_event_fn = Some(parse_collect_event);
122    }
123
124    /// Sets the function used to parse flash events for this Dex.
125    pub fn set_flash_event_parsing(
126        &mut self,
127        parse_flash_event: fn(SharedDex, Log) -> anyhow::Result<FlashEvent>,
128    ) {
129        self.parse_flash_event_fn = Some(parse_flash_event);
130    }
131
132    /// Sets the function used to convert trade data for this Dex.
133    pub fn set_convert_trade_data(&mut self, convert_trade_data: ConvertTradeDataFn) {
134        self.convert_to_trade_data_fn = Some(convert_trade_data);
135    }
136
137    /// Parses a pool creation event log using this DEX's specific parsing function.
138    ///
139    /// # Errors
140    ///
141    /// Returns an error if the DEX does not have a pool creation event parser defined or if parsing fails.
142    pub fn parse_pool_created_event(&self, log: Log) -> anyhow::Result<PoolCreatedEvent> {
143        if let Some(parse_pool_created_event_fn) = &self.parse_pool_created_event_fn {
144            parse_pool_created_event_fn(log)
145        } else {
146            anyhow::bail!(
147                "Parsing of pool created event in not defined in this dex: {}:{}",
148                self.dex.chain,
149                self.dex.name,
150            )
151        }
152    }
153
154    /// Parses a swap event log using this DEX's specific parsing function.
155    ///
156    /// # Errors
157    ///
158    /// Returns an error if the DEX does not have a swap event parser defined or if parsing fails.
159    pub fn parse_swap_event(&self, log: Log) -> anyhow::Result<SwapEvent> {
160        if let Some(parse_swap_event_fn) = &self.parse_swap_event_fn {
161            parse_swap_event_fn(self.dex.clone(), log)
162        } else {
163            anyhow::bail!(
164                "Parsing of swap event in not defined in this dex: {}:{}",
165                self.dex.chain,
166                self.dex.name
167            )
168        }
169    }
170
171    /// Convert to trade data from a log using this DEX's specific parsing function.
172    ///
173    /// # Errors
174    ///
175    /// Returns an error if the DEX does not have a trade data converter defined or if conversion fails.
176    pub fn convert_to_trade_data(
177        &self,
178        token0: &Token,
179        token1: &Token,
180        swap_event: &SwapEvent,
181    ) -> anyhow::Result<(OrderSide, Quantity, Price)> {
182        if let Some(convert_to_trade_data_fn) = &self.convert_to_trade_data_fn {
183            convert_to_trade_data_fn(token0, token1, swap_event)
184        } else {
185            anyhow::bail!(
186                "Converting to trade data is not defined in this dex: {}:{}",
187                self.dex.chain,
188                self.dex.name
189            )
190        }
191    }
192
193    /// Parses a mint event log using this DEX's specific parsing function.
194    ///
195    /// # Errors
196    ///
197    /// Returns an error if the DEX does not have a mint event parser defined or if parsing fails.
198    pub fn parse_mint_event(&self, log: Log) -> anyhow::Result<MintEvent> {
199        if let Some(parse_mint_event_fn) = &self.parse_mint_event_fn {
200            parse_mint_event_fn(self.dex.clone(), log)
201        } else {
202            anyhow::bail!(
203                "Parsing of mint event in not defined in this dex: {}:{}",
204                self.dex.chain,
205                self.dex.name
206            )
207        }
208    }
209
210    /// Parses a burn event log using this DEX's specific parsing function.
211    ///
212    /// # Errors
213    ///
214    /// Returns an error if the DEX does not have a burn event parser defined or if parsing fails.
215    pub fn parse_burn_event(&self, log: Log) -> anyhow::Result<BurnEvent> {
216        if let Some(parse_burn_event_fn) = &self.parse_burn_event_fn {
217            parse_burn_event_fn(self.dex.clone(), log)
218        } else {
219            anyhow::bail!(
220                "Parsing of burn event in not defined in this dex: {}",
221                self.dex.name
222            )
223        }
224    }
225
226    /// Checks if this DEX requires pool initialization events.
227    pub fn needs_initialization(&self) -> bool {
228        self.dex.initialize_event.is_some()
229    }
230
231    /// Parses an event log into an `InitializeEvent` struct.
232    pub fn parse_initialize_event(&self, log: Log) -> anyhow::Result<InitializeEvent> {
233        if let Some(parse_initialize_event_fn) = &self.parse_initialize_event_fn {
234            parse_initialize_event_fn(self.dex.clone(), log)
235        } else {
236            anyhow::bail!(
237                "Parsing of initialize event in not defined in this dex: {}",
238                self.dex.name
239            )
240        }
241    }
242
243    /// Parses a collect event log using this DEX's specific parsing function.
244    ///
245    /// # Errors
246    ///
247    /// Returns an error if the DEX does not have a collect event parser defined or if parsing fails.
248    pub fn parse_collect_event(&self, log: Log) -> anyhow::Result<CollectEvent> {
249        if let Some(parse_collect_event_fn) = &self.parse_collect_event_fn {
250            parse_collect_event_fn(self.dex.clone(), log)
251        } else {
252            anyhow::bail!(
253                "Parsing of collect event in not defined in this dex: {}:{}",
254                self.dex.chain,
255                self.dex.name
256            )
257        }
258    }
259}
260
261impl Deref for DexExtended {
262    type Target = Dex;
263
264    fn deref(&self) -> &Self::Target {
265        &self.dex
266    }
267}