odos_sdk/
router_type.rs

1// SPDX-FileCopyrightText: 2025 Semiotic AI, Inc.
2//
3// SPDX-License-Identifier: Apache-2.0
4//! Router type definitions for Odos protocol
5//!
6//! This module provides enums and types to represent the different router types
7//! available across Odos-supported chains.
8
9use std::fmt;
10
11/// Represents the different types of Odos routers.
12///
13/// Different chains support different combinations of these router types.
14/// Use the `OdosChain` trait methods to check router availability per chain.
15///
16/// # Event Schemas
17///
18/// **Important:** Router types emit fundamentally different events:
19///
20/// | Router Type | Events Emitted |
21/// |-------------|----------------|
22/// | [`V2`](RouterType::V2) | `Swap`, `SwapMulti` |
23/// | [`V3`](RouterType::V3) | `Swap`, `SwapMulti` (with referral/slippage fields) |
24/// | [`LimitOrder`](RouterType::LimitOrder) | `LimitOrderFilled`, `LimitOrderCancelled`, etc. |
25///
26/// When iterating over router types for event processing, use [`swap_routers()`](RouterType::swap_routers)
27/// to get only routers that emit `Swap`/`SwapMulti` events, or [`order_routers()`](RouterType::order_routers)
28/// for limit order routers.
29///
30/// # Example
31///
32/// ```rust
33/// use odos_sdk::RouterType;
34///
35/// // When processing swap events, iterate only over swap routers
36/// for router_type in RouterType::swap_routers() {
37///     // V2 and V3 both emit Swap/SwapMulti events
38///     assert!(router_type.emits_swap_events());
39/// }
40///
41/// // LimitOrder routers require separate event handling
42/// for router_type in RouterType::order_routers() {
43///     assert!(!router_type.emits_swap_events());
44/// }
45/// ```
46#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
47pub enum RouterType {
48    /// Limit Order V2 router for limit order functionality.
49    ///
50    /// Available on all supported chains.
51    ///
52    /// **Note:** This router emits `LimitOrderFilled`, `LimitOrderCancelled`, and other
53    /// limit order-specific events. It does **not** emit `Swap` or `SwapMulti` events
54    /// like V2/V3 routers. Use [`emits_swap_events()`](RouterType::emits_swap_events) to
55    /// check event compatibility, or [`swap_routers()`](RouterType::swap_routers) to
56    /// iterate only over routers that emit swap events.
57    LimitOrder,
58
59    /// V2 router for swap functionality.
60    ///
61    /// Available on all supported chains.
62    ///
63    /// Emits `Swap` and `SwapMulti` events.
64    V2,
65
66    /// V3 router for enhanced swap functionality.
67    ///
68    /// Available on all supported chains (unified address via CREATE2).
69    ///
70    /// Emits `Swap` and `SwapMulti` events (with additional referral/slippage fields
71    /// compared to V2).
72    V3,
73}
74
75impl RouterType {
76    /// Returns all possible router types.
77    ///
78    /// **Note:** This includes both swap routers (V2, V3) and order routers (LimitOrder),
79    /// which emit different event types. For event processing, consider using
80    /// [`swap_routers()`](RouterType::swap_routers) or [`order_routers()`](RouterType::order_routers)
81    /// instead.
82    pub const fn all() -> [RouterType; 3] {
83        [RouterType::LimitOrder, RouterType::V2, RouterType::V3]
84    }
85
86    /// Returns router types that emit `Swap` and `SwapMulti` events.
87    ///
88    /// Use this when iterating over routers for swap event processing.
89    /// Both V2 and V3 routers emit these events (with slightly different schemas).
90    ///
91    /// # Example
92    ///
93    /// ```rust
94    /// use odos_sdk::RouterType;
95    ///
96    /// // Process only routers that emit swap events
97    /// for router_type in RouterType::swap_routers() {
98    ///     println!("Processing swap events for {:?}", router_type);
99    /// }
100    /// ```
101    pub const fn swap_routers() -> [RouterType; 2] {
102        [RouterType::V2, RouterType::V3]
103    }
104
105    /// Returns router types that emit limit order events (`LimitOrderFilled`, etc.).
106    ///
107    /// Use this when iterating over routers for limit order event processing.
108    ///
109    /// # Example
110    ///
111    /// ```rust
112    /// use odos_sdk::RouterType;
113    ///
114    /// // Process only routers that emit limit order events
115    /// for router_type in RouterType::order_routers() {
116    ///     println!("Processing order events for {:?}", router_type);
117    /// }
118    /// ```
119    pub const fn order_routers() -> [RouterType; 1] {
120        [RouterType::LimitOrder]
121    }
122
123    /// Returns whether this router type emits `Swap` and `SwapMulti` events.
124    ///
125    /// - `true` for [`V2`](RouterType::V2) and [`V3`](RouterType::V3)
126    /// - `false` for [`LimitOrder`](RouterType::LimitOrder)
127    ///
128    /// # Example
129    ///
130    /// ```rust
131    /// use odos_sdk::RouterType;
132    ///
133    /// assert!(RouterType::V2.emits_swap_events());
134    /// assert!(RouterType::V3.emits_swap_events());
135    /// assert!(!RouterType::LimitOrder.emits_swap_events());
136    /// ```
137    pub const fn emits_swap_events(&self) -> bool {
138        matches!(self, RouterType::V2 | RouterType::V3)
139    }
140
141    /// Returns whether this router type emits limit order events.
142    ///
143    /// - `true` for [`LimitOrder`](RouterType::LimitOrder)
144    /// - `false` for [`V2`](RouterType::V2) and [`V3`](RouterType::V3)
145    ///
146    /// # Example
147    ///
148    /// ```rust
149    /// use odos_sdk::RouterType;
150    ///
151    /// assert!(RouterType::LimitOrder.emits_order_events());
152    /// assert!(!RouterType::V2.emits_order_events());
153    /// assert!(!RouterType::V3.emits_order_events());
154    /// ```
155    pub const fn emits_order_events(&self) -> bool {
156        matches!(self, RouterType::LimitOrder)
157    }
158
159    /// Returns the router type as a string identifier.
160    pub const fn as_str(&self) -> &'static str {
161        match self {
162            RouterType::LimitOrder => "LO",
163            RouterType::V2 => "V2",
164            RouterType::V3 => "V3",
165        }
166    }
167}
168
169impl fmt::Display for RouterType {
170    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
171        write!(f, "{}", self.as_str())
172    }
173}
174
175/// Represents which routers are available on a specific chain
176///
177/// This provides a type-safe way to query router availability without
178/// needing to call multiple trait methods.
179#[derive(Debug, Clone, Copy, PartialEq, Eq)]
180pub struct RouterAvailability {
181    /// Whether the Limit Order V2 router is available
182    pub limit_order: bool,
183    /// Whether the V2 router is available
184    pub v2: bool,
185    /// Whether the V3 router is available
186    pub v3: bool,
187}
188
189impl RouterAvailability {
190    /// Creates a new `RouterAvailability` with all routers available
191    pub const fn all() -> Self {
192        Self {
193            limit_order: true,
194            v2: true,
195            v3: true,
196        }
197    }
198
199    /// Creates a new `RouterAvailability` with no routers available
200    pub const fn none() -> Self {
201        Self {
202            limit_order: false,
203            v2: false,
204            v3: false,
205        }
206    }
207
208    /// Creates availability for LO + V3 only
209    pub const fn lo_v3_only() -> Self {
210        Self {
211            limit_order: true,
212            v2: false,
213            v3: true,
214        }
215    }
216
217    /// Creates availability for V2 + V3 only (most chains)
218    pub const fn v2_v3_only() -> Self {
219        Self {
220            limit_order: false,
221            v2: true,
222            v3: true,
223        }
224    }
225
226    /// Checks if the specified router type is available
227    pub const fn has(&self, router_type: RouterType) -> bool {
228        match router_type {
229            RouterType::LimitOrder => self.limit_order,
230            RouterType::V2 => self.v2,
231            RouterType::V3 => self.v3,
232        }
233    }
234
235    /// Returns all available router types
236    pub fn available_routers(&self) -> Vec<RouterType> {
237        let mut routers = Vec::new();
238        if self.limit_order {
239            routers.push(RouterType::LimitOrder);
240        }
241        if self.v2 {
242            routers.push(RouterType::V2);
243        }
244        if self.v3 {
245            routers.push(RouterType::V3);
246        }
247        routers
248    }
249
250    /// Returns the count of available routers
251    pub const fn count(&self) -> usize {
252        let mut count = 0;
253        if self.limit_order {
254            count += 1;
255        }
256        if self.v2 {
257            count += 1;
258        }
259        if self.v3 {
260            count += 1;
261        }
262        count
263    }
264
265    /// Checks if any router is available
266    pub const fn has_any(&self) -> bool {
267        self.limit_order || self.v2 || self.v3
268    }
269}
270
271impl fmt::Display for RouterAvailability {
272    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
273        let routers = self.available_routers();
274        if routers.is_empty() {
275            write!(f, "No routers available")
276        } else {
277            write!(
278                f,
279                "{}",
280                routers
281                    .iter()
282                    .map(|r| r.as_str())
283                    .collect::<Vec<_>>()
284                    .join(", ")
285            )
286        }
287    }
288}
289
290#[cfg(test)]
291mod tests {
292    use super::*;
293
294    #[test]
295    fn test_router_type_all() {
296        let all = RouterType::all();
297        assert_eq!(all.len(), 3);
298        assert!(all.contains(&RouterType::LimitOrder));
299        assert!(all.contains(&RouterType::V2));
300        assert!(all.contains(&RouterType::V3));
301    }
302
303    #[test]
304    fn test_router_type_display() {
305        assert_eq!(RouterType::LimitOrder.to_string(), "LO");
306        assert_eq!(RouterType::V2.to_string(), "V2");
307        assert_eq!(RouterType::V3.to_string(), "V3");
308    }
309
310    #[test]
311    fn test_router_availability_all() {
312        let avail = RouterAvailability::all();
313        assert!(avail.limit_order);
314        assert!(avail.v2);
315        assert!(avail.v3);
316        assert_eq!(avail.count(), 3);
317    }
318
319    #[test]
320    fn test_router_availability_none() {
321        let avail = RouterAvailability::none();
322        assert!(!avail.limit_order);
323        assert!(!avail.v2);
324        assert!(!avail.v3);
325        assert_eq!(avail.count(), 0);
326        assert!(!avail.has_any());
327    }
328
329    #[test]
330    fn test_router_availability_lo_v3_only() {
331        let avail = RouterAvailability::lo_v3_only();
332        assert!(avail.limit_order);
333        assert!(!avail.v2);
334        assert!(avail.v3);
335        assert_eq!(avail.count(), 2);
336        assert!(avail.has(RouterType::LimitOrder));
337        assert!(!avail.has(RouterType::V2));
338        assert!(avail.has(RouterType::V3));
339    }
340
341    #[test]
342    fn test_router_availability_v2_v3_only() {
343        let avail = RouterAvailability::v2_v3_only();
344        assert!(!avail.limit_order);
345        assert!(avail.v2);
346        assert!(avail.v3);
347        assert_eq!(avail.count(), 2);
348    }
349
350    #[test]
351    fn test_available_routers() {
352        let avail = RouterAvailability::all();
353        let routers = avail.available_routers();
354        assert_eq!(routers.len(), 3);
355
356        let avail = RouterAvailability::lo_v3_only();
357        let routers = avail.available_routers();
358        assert_eq!(routers.len(), 2);
359        assert!(routers.contains(&RouterType::LimitOrder));
360        assert!(routers.contains(&RouterType::V3));
361    }
362
363    #[test]
364    fn test_display() {
365        let avail = RouterAvailability::all();
366        assert_eq!(avail.to_string(), "LO, V2, V3");
367
368        let avail = RouterAvailability::lo_v3_only();
369        assert_eq!(avail.to_string(), "LO, V3");
370
371        let avail = RouterAvailability::none();
372        assert_eq!(avail.to_string(), "No routers available");
373    }
374
375    #[test]
376    fn test_swap_routers() {
377        let swap = RouterType::swap_routers();
378        assert_eq!(swap.len(), 2);
379        assert!(swap.contains(&RouterType::V2));
380        assert!(swap.contains(&RouterType::V3));
381        assert!(!swap.contains(&RouterType::LimitOrder));
382    }
383
384    #[test]
385    fn test_order_routers() {
386        let order = RouterType::order_routers();
387        assert_eq!(order.len(), 1);
388        assert!(order.contains(&RouterType::LimitOrder));
389        assert!(!order.contains(&RouterType::V2));
390        assert!(!order.contains(&RouterType::V3));
391    }
392
393    #[test]
394    fn test_emits_swap_events() {
395        assert!(RouterType::V2.emits_swap_events());
396        assert!(RouterType::V3.emits_swap_events());
397        assert!(!RouterType::LimitOrder.emits_swap_events());
398    }
399
400    #[test]
401    fn test_emits_order_events() {
402        assert!(RouterType::LimitOrder.emits_order_events());
403        assert!(!RouterType::V2.emits_order_events());
404        assert!(!RouterType::V3.emits_order_events());
405    }
406
407    #[test]
408    fn test_swap_and_order_routers_are_exhaustive() {
409        // Verify that swap_routers + order_routers covers all router types
410        let all: std::collections::HashSet<_> = RouterType::all().into_iter().collect();
411        let swap: std::collections::HashSet<_> = RouterType::swap_routers().into_iter().collect();
412        let order: std::collections::HashSet<_> = RouterType::order_routers().into_iter().collect();
413
414        // Union of swap and order should equal all
415        let combined: std::collections::HashSet<_> = swap.union(&order).copied().collect();
416        assert_eq!(all, combined);
417
418        // Swap and order should be disjoint
419        assert!(swap.is_disjoint(&order));
420    }
421}