openzeppelin_relayer/queues/
swap_filter.rs

1use crate::models::{RelayerNetworkPolicy, RelayerRepoModel};
2use tracing::debug;
3
4/// Filters relayers to find those eligible for swap workers (Solana or Stellar).
5pub fn filter_relayers_for_swap(relayers: Vec<RelayerRepoModel>) -> Vec<RelayerRepoModel> {
6    relayers
7        .into_iter()
8        .filter(|relayer| match &relayer.policies {
9            RelayerNetworkPolicy::Solana(policy) => {
10                let swap_config = match policy.get_swap_config() {
11                    Some(config) => config,
12                    None => {
13                        debug!(relayer_id = %relayer.id, "No Solana swap configuration specified; skipping");
14                        return false;
15                    }
16                };
17
18                if swap_config.cron_schedule.is_none() {
19                    debug!(relayer_id = %relayer.id, "No cron schedule specified; skipping");
20                    return false;
21                }
22                true
23            }
24            RelayerNetworkPolicy::Stellar(policy) => {
25                let swap_config = match policy.get_swap_config() {
26                    Some(config) => config,
27                    None => {
28                        debug!(relayer_id = %relayer.id, "No Stellar swap configuration specified; skipping");
29                        return false;
30                    }
31                };
32
33                if swap_config.cron_schedule.is_none() {
34                    debug!(relayer_id = %relayer.id, "No cron schedule specified; skipping");
35                    return false;
36                }
37                true
38            }
39            _ => {
40                debug!(relayer_id = %relayer.id, "Network type does not support swap; skipping");
41                false
42            }
43        })
44        .collect()
45}
46
47#[cfg(test)]
48mod tests {
49    use super::*;
50    use crate::models::{
51        RelayerSolanaPolicy, RelayerSolanaSwapConfig, RelayerStellarPolicy,
52        RelayerStellarSwapConfig,
53    };
54
55    fn solana_relayer_with_swap(id: &str, cron: Option<&str>) -> RelayerRepoModel {
56        RelayerRepoModel {
57            id: id.to_string(),
58            policies: RelayerNetworkPolicy::Solana(RelayerSolanaPolicy {
59                swap_config: Some(RelayerSolanaSwapConfig {
60                    strategy: None,
61                    cron_schedule: cron.map(String::from),
62                    min_balance_threshold: None,
63                    jupiter_swap_options: None,
64                }),
65                ..Default::default()
66            }),
67            ..Default::default()
68        }
69    }
70
71    fn solana_relayer_no_swap(id: &str) -> RelayerRepoModel {
72        RelayerRepoModel {
73            id: id.to_string(),
74            policies: RelayerNetworkPolicy::Solana(RelayerSolanaPolicy::default()),
75            ..Default::default()
76        }
77    }
78
79    fn stellar_relayer_with_swap(id: &str, cron: Option<&str>) -> RelayerRepoModel {
80        RelayerRepoModel {
81            id: id.to_string(),
82            policies: RelayerNetworkPolicy::Stellar(RelayerStellarPolicy {
83                swap_config: Some(RelayerStellarSwapConfig {
84                    strategies: vec![],
85                    cron_schedule: cron.map(String::from),
86                    min_balance_threshold: None,
87                }),
88                ..Default::default()
89            }),
90            ..Default::default()
91        }
92    }
93
94    fn stellar_relayer_no_swap(id: &str) -> RelayerRepoModel {
95        RelayerRepoModel {
96            id: id.to_string(),
97            policies: RelayerNetworkPolicy::Stellar(RelayerStellarPolicy::default()),
98            ..Default::default()
99        }
100    }
101
102    fn evm_relayer(id: &str) -> RelayerRepoModel {
103        RelayerRepoModel {
104            id: id.to_string(),
105            ..Default::default() // default is EVM
106        }
107    }
108
109    #[test]
110    fn test_empty_list_returns_empty() {
111        assert!(filter_relayers_for_swap(vec![]).is_empty());
112    }
113
114    #[test]
115    fn test_solana_with_swap_and_cron_included() {
116        let relayers = vec![solana_relayer_with_swap("sol-1", Some("0 0 * * * *"))];
117        let result = filter_relayers_for_swap(relayers);
118        assert_eq!(result.len(), 1);
119        assert_eq!(result[0].id, "sol-1");
120    }
121
122    #[test]
123    fn test_solana_with_swap_but_no_cron_excluded() {
124        let relayers = vec![solana_relayer_with_swap("sol-1", None)];
125        assert!(filter_relayers_for_swap(relayers).is_empty());
126    }
127
128    #[test]
129    fn test_solana_without_swap_config_excluded() {
130        let relayers = vec![solana_relayer_no_swap("sol-1")];
131        assert!(filter_relayers_for_swap(relayers).is_empty());
132    }
133
134    #[test]
135    fn test_stellar_with_swap_and_cron_included() {
136        let relayers = vec![stellar_relayer_with_swap("xlm-1", Some("0 */5 * * * *"))];
137        let result = filter_relayers_for_swap(relayers);
138        assert_eq!(result.len(), 1);
139        assert_eq!(result[0].id, "xlm-1");
140    }
141
142    #[test]
143    fn test_stellar_with_swap_but_no_cron_excluded() {
144        let relayers = vec![stellar_relayer_with_swap("xlm-1", None)];
145        assert!(filter_relayers_for_swap(relayers).is_empty());
146    }
147
148    #[test]
149    fn test_stellar_without_swap_config_excluded() {
150        let relayers = vec![stellar_relayer_no_swap("xlm-1")];
151        assert!(filter_relayers_for_swap(relayers).is_empty());
152    }
153
154    #[test]
155    fn test_evm_relayer_excluded() {
156        let relayers = vec![evm_relayer("evm-1")];
157        assert!(filter_relayers_for_swap(relayers).is_empty());
158    }
159
160    #[test]
161    fn test_mixed_list_filters_correctly() {
162        let relayers = vec![
163            solana_relayer_with_swap("sol-ok", Some("0 0 * * * *")),
164            solana_relayer_with_swap("sol-no-cron", None),
165            solana_relayer_no_swap("sol-no-swap"),
166            stellar_relayer_with_swap("xlm-ok", Some("0 0 * * * *")),
167            stellar_relayer_no_swap("xlm-no-swap"),
168            evm_relayer("evm-1"),
169        ];
170        let result = filter_relayers_for_swap(relayers);
171        let ids: Vec<&str> = result.iter().map(|r| r.id.as_str()).collect();
172        assert_eq!(ids, vec!["sol-ok", "xlm-ok"]);
173    }
174}