1use crate::models::NetworkType;
2use serde::{Deserialize, Serialize};
3use utoipa::ToSchema;
4
5use super::{
6 RelayerNetworkPolicy, RelayerRepoModel, RelayerSolanaSwapConfig, SolanaAllowedTokensPolicy,
7 SolanaFeePaymentStrategy,
8};
9
10#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, ToSchema)]
11pub struct DeletePendingTransactionsResponse {
12 pub queued_for_cancellation_transaction_ids: Vec<String>,
13 pub failed_to_queue_transaction_ids: Vec<String>,
14 pub total_processed: u32,
15}
16
17#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, ToSchema)]
18pub struct RelayerResponse {
19 pub id: String,
20 pub name: String,
21 pub network: String,
22 #[serde(rename = "network_type")]
23 pub network_type: NetworkType,
24 pub paused: bool,
25 pub policies: NetworkPolicyResponse,
26 pub address: String,
27 pub system_disabled: bool,
28}
29
30#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, ToSchema)]
31#[serde(tag = "network_type")]
32pub enum RelayerStatus {
33 #[serde(rename = "evm")]
34 Evm {
35 balance: String,
36 pending_transactions_count: u64,
37 last_confirmed_transaction_timestamp: Option<String>,
38 system_disabled: bool,
39 paused: bool,
40 nonce: String,
41 },
42 #[serde(rename = "stellar")]
43 Stellar {
44 balance: String,
45 pending_transactions_count: u64,
46 last_confirmed_transaction_timestamp: Option<String>,
47 system_disabled: bool,
48 paused: bool,
49 sequence_number: String,
50 },
51}
52
53#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, ToSchema)]
54#[serde(untagged)]
55pub enum NetworkPolicyResponse {
56 Evm(EvmPolicyResponse),
57 Solana(SolanaPolicyResponse),
58 Stellar(StellarPolicyResponse),
59}
60
61#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, ToSchema)]
62pub struct EvmPolicyResponse {
63 #[serde(skip_serializing_if = "Option::is_none")]
64 #[schema(nullable = false)]
65 pub gas_price_cap: Option<u128>,
66 #[serde(skip_serializing_if = "Option::is_none")]
67 #[schema(nullable = false)]
68 pub whitelist_receivers: Option<Vec<String>>,
69 #[serde(skip_serializing_if = "Option::is_none")]
70 #[schema(nullable = false)]
71 pub eip1559_pricing: Option<bool>,
72 pub private_transactions: bool,
73 pub min_balance: u128,
74}
75
76#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, ToSchema)]
77pub struct SolanaPolicyResponse {
78 fee_payment_strategy: SolanaFeePaymentStrategy,
79 #[serde(skip_serializing_if = "Option::is_none")]
80 pub fee_margin_percentage: Option<f32>,
81 #[serde(skip_serializing_if = "Option::is_none")]
82 #[schema(nullable = false)]
83 pub allowed_tokens: Option<Vec<SolanaAllowedTokensPolicy>>,
84 #[serde(skip_serializing_if = "Option::is_none")]
85 #[schema(nullable = false)]
86 pub allowed_programs: Option<Vec<String>>,
87 #[serde(skip_serializing_if = "Option::is_none")]
88 #[schema(nullable = false)]
89 pub allowed_accounts: Option<Vec<String>>,
90 #[serde(skip_serializing_if = "Option::is_none")]
91 #[schema(nullable = false)]
92 pub disallowed_accounts: Option<Vec<String>>,
93 #[serde(skip_serializing_if = "Option::is_none")]
94 #[schema(nullable = false)]
95 pub max_signatures: Option<u8>,
96 pub max_tx_data_size: u16,
97 pub min_balance: u64,
98 #[serde(skip_serializing_if = "Option::is_none")]
99 #[schema(nullable = false)]
100 pub max_allowed_fee_lamports: Option<u64>,
101 #[serde(skip_serializing_if = "Option::is_none")]
102 #[schema(nullable = false)]
103 pub swap_config: Option<RelayerSolanaSwapConfig>,
104}
105
106#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, ToSchema)]
107pub struct StellarPolicyResponse {
108 #[serde(skip_serializing_if = "Option::is_none")]
109 #[schema(nullable = false)]
110 pub max_fee: Option<u32>,
111 pub min_balance: u64,
112}
113
114impl From<RelayerRepoModel> for RelayerResponse {
115 fn from(model: RelayerRepoModel) -> Self {
116 let policies = match model.policies {
117 RelayerNetworkPolicy::Evm(evm) => NetworkPolicyResponse::Evm(EvmPolicyResponse {
118 gas_price_cap: evm.gas_price_cap,
119 whitelist_receivers: evm.whitelist_receivers,
120 eip1559_pricing: evm.eip1559_pricing,
121 min_balance: evm.min_balance,
122 private_transactions: evm.private_transactions,
123 }),
124 RelayerNetworkPolicy::Solana(solana) => {
125 NetworkPolicyResponse::Solana(SolanaPolicyResponse {
126 fee_payment_strategy: solana.fee_payment_strategy,
127 fee_margin_percentage: solana.fee_margin_percentage,
128 min_balance: solana.min_balance,
129 allowed_tokens: solana.allowed_tokens,
130 allowed_programs: solana.allowed_programs,
131 allowed_accounts: solana.allowed_accounts,
132 disallowed_accounts: solana.disallowed_accounts,
133 max_signatures: solana.max_signatures,
134 max_tx_data_size: solana.max_tx_data_size,
135 max_allowed_fee_lamports: solana.max_allowed_fee_lamports,
136 swap_config: solana.swap_config,
137 })
138 }
139 RelayerNetworkPolicy::Stellar(stellar) => {
140 NetworkPolicyResponse::Stellar(StellarPolicyResponse {
141 max_fee: stellar.max_fee,
142 min_balance: stellar.min_balance,
143 })
144 }
145 };
146
147 Self {
148 id: model.id,
149 name: model.name,
150 network: model.network,
151 network_type: model.network_type,
152 paused: model.paused,
153 policies,
154 address: model.address,
155 system_disabled: model.system_disabled,
156 }
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use crate::models::{
163 RelayerEvmPolicy, RelayerSolanaPolicy, RelayerStellarPolicy, SolanaAllowedTokensSwapConfig,
164 SolanaFeePaymentStrategy,
165 };
166
167 use super::*;
168
169 #[test]
170 fn test_from_relayer_repo_model_evm() {
171 let model = RelayerRepoModel {
172 id: "test-id".to_string(),
173 name: "Test Relayer".to_string(),
174 network: "ethereum".to_string(),
175 network_type: NetworkType::Evm,
176 paused: false,
177 policies: RelayerNetworkPolicy::Evm(RelayerEvmPolicy {
178 gas_price_cap: Some(100),
179 whitelist_receivers: Some(vec!["0x123".to_string()]),
180 eip1559_pricing: Some(true),
181 min_balance: 1000,
182 private_transactions: true,
183 }),
184 address: "0xabc".to_string(),
185 system_disabled: false,
186 signer_id: "test-signer-id".to_string(),
187 notification_id: Some("test-notification-id".to_string()),
188 custom_rpc_urls: None,
189 };
190
191 let response: RelayerResponse = model.clone().into();
192
193 assert_eq!(response.id, model.id);
194 assert_eq!(response.name, model.name);
195 assert_eq!(response.network, model.network);
196 assert_eq!(response.network_type, model.network_type);
197 assert_eq!(response.paused, model.paused);
198 assert_eq!(response.address, model.address);
199 assert_eq!(response.system_disabled, model.system_disabled);
200
201 if let NetworkPolicyResponse::Evm(evm) = response.policies {
202 if let RelayerNetworkPolicy::Evm(expected) = model.policies {
203 assert_eq!(evm.gas_price_cap, expected.gas_price_cap);
204 assert_eq!(evm.whitelist_receivers, expected.whitelist_receivers);
205 assert_eq!(evm.eip1559_pricing, expected.eip1559_pricing);
206 assert_eq!(evm.min_balance, expected.min_balance);
207 assert_eq!(evm.private_transactions, expected.private_transactions);
208 } else {
209 panic!("Expected EVM policy");
210 }
211 } else {
212 panic!("Expected EVM policy response");
213 }
214 }
215
216 #[test]
217 fn test_from_relayer_repo_model_solana() {
218 let model = RelayerRepoModel {
219 id: "test-id".to_string(),
220 name: "Test Relayer".to_string(),
221 network: "solana".to_string(),
222 network_type: NetworkType::Solana,
223 paused: true,
224 policies: RelayerNetworkPolicy::Solana(RelayerSolanaPolicy {
225 fee_payment_strategy: SolanaFeePaymentStrategy::User,
226 fee_margin_percentage: Some(0.5),
227 min_balance: 5000,
228 allowed_tokens: Some(vec![SolanaAllowedTokensPolicy {
229 mint: "mint-address".to_string(),
230 decimals: Some(9),
231 symbol: Some("SOL".to_string()),
232 max_allowed_fee: Some(1000),
233 swap_config: Some(SolanaAllowedTokensSwapConfig {
234 slippage_percentage: Some(100.0),
235 max_amount: None,
236 min_amount: None,
237 retain_min_amount: None,
238 }),
239 }]),
240 allowed_programs: Some(vec!["program1".to_string()]),
241 allowed_accounts: Some(vec!["account1".to_string()]),
242 disallowed_accounts: Some(vec!["bad-account".to_string()]),
243 max_signatures: Some(10),
244 max_tx_data_size: 1024,
245 max_allowed_fee_lamports: Some(10000),
246 swap_config: None,
247 }),
248 address: "solana-address".to_string(),
249 system_disabled: false,
250 signer_id: "test-signer-id".to_string(),
251 notification_id: Some("test-notification-id".to_string()),
252 custom_rpc_urls: None,
253 };
254
255 let response: RelayerResponse = model.clone().into();
256
257 assert_eq!(response.id, model.id);
258 assert_eq!(response.name, model.name);
259 assert_eq!(response.network, model.network);
260 assert_eq!(response.network_type, model.network_type);
261 assert_eq!(response.paused, model.paused);
262 assert_eq!(response.address, model.address);
263 assert_eq!(response.system_disabled, model.system_disabled);
264
265 if let NetworkPolicyResponse::Solana(solana) = response.policies {
266 if let RelayerNetworkPolicy::Solana(expected) = model.policies {
267 assert_eq!(solana.min_balance, expected.min_balance);
268 assert_eq!(solana.allowed_tokens, expected.allowed_tokens);
269 assert_eq!(solana.allowed_programs, expected.allowed_programs);
270 assert_eq!(solana.allowed_accounts, expected.allowed_accounts);
271 assert_eq!(solana.disallowed_accounts, expected.disallowed_accounts);
272 assert_eq!(solana.max_signatures, expected.max_signatures);
273 assert_eq!(solana.max_tx_data_size, expected.max_tx_data_size);
274 assert_eq!(
275 solana.max_allowed_fee_lamports,
276 expected.max_allowed_fee_lamports
277 );
278 } else {
279 panic!("Expected Solana policy");
280 }
281 } else {
282 panic!("Expected Solana policy response");
283 }
284 }
285
286 #[test]
287 fn test_from_relayer_repo_model_stellar() {
288 let model = RelayerRepoModel {
289 id: "test-id".to_string(),
290 name: "Test Relayer".to_string(),
291 network: "stellar".to_string(),
292 network_type: NetworkType::Stellar,
293 paused: false,
294 policies: RelayerNetworkPolicy::Stellar(RelayerStellarPolicy {
295 max_fee: Some(200),
296 min_balance: 2000,
297 timeout_seconds: Some(100),
298 }),
299 address: "stellar-address".to_string(),
300 system_disabled: true,
301 signer_id: "test-signer-id".to_string(),
302 notification_id: Some("test-notification-id".to_string()),
303 custom_rpc_urls: None,
304 };
305
306 let response: RelayerResponse = model.clone().into();
307
308 assert_eq!(response.id, model.id);
309 assert_eq!(response.name, model.name);
310 assert_eq!(response.network, model.network);
311 assert_eq!(response.network_type, model.network_type);
312 assert_eq!(response.paused, model.paused);
313 assert_eq!(response.address, model.address);
314 assert_eq!(response.system_disabled, model.system_disabled);
315
316 if let NetworkPolicyResponse::Stellar(stellar) = response.policies {
317 if let RelayerNetworkPolicy::Stellar(expected) = model.policies {
318 assert_eq!(stellar.max_fee, expected.max_fee);
319 assert_eq!(stellar.min_balance, expected.min_balance);
320 } else {
321 panic!("Expected Stellar policy");
322 }
323 } else {
324 panic!("Expected Stellar policy response");
325 }
326 }
327}