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}