1use super::{
15 DisabledReason, MaskedRpcConfig, Relayer, RelayerEvmPolicy, RelayerNetworkPolicy,
16 RelayerNetworkType, RelayerRepoModel, RelayerSolanaPolicy, RelayerSolanaSwapConfig,
17 RelayerStellarPolicy, RelayerStellarSwapConfig, SolanaAllowedTokensPolicy,
18 SolanaFeePaymentStrategy, StellarAllowedTokensPolicy, StellarFeePaymentStrategy,
19};
20use crate::constants::{
21 DEFAULT_EVM_GAS_LIMIT_ESTIMATION, DEFAULT_EVM_INCLUDE_REVERT_DATA, DEFAULT_EVM_MIN_BALANCE,
22 DEFAULT_SOLANA_MAX_TX_DATA_SIZE, DEFAULT_SOLANA_MIN_BALANCE, DEFAULT_STELLAR_MIN_BALANCE,
23};
24use serde::{Deserialize, Serialize};
25use utoipa::ToSchema;
26
27#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, ToSchema)]
29pub struct DeletePendingTransactionsResponse {
30 pub queued_for_cancellation_transaction_ids: Vec<String>,
31 pub failed_to_queue_transaction_ids: Vec<String>,
32 pub total_processed: u32,
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, ToSchema)]
38#[serde(untagged)]
39pub enum RelayerNetworkPolicyResponse {
40 Evm(EvmPolicyResponse),
43 Stellar(StellarPolicyResponse),
45 Solana(SolanaPolicyResponse),
47}
48
49impl From<RelayerNetworkPolicy> for RelayerNetworkPolicyResponse {
50 fn from(policy: RelayerNetworkPolicy) -> Self {
51 match policy {
52 RelayerNetworkPolicy::Evm(evm_policy) => {
53 RelayerNetworkPolicyResponse::Evm(evm_policy.into())
54 }
55 RelayerNetworkPolicy::Solana(solana_policy) => {
56 RelayerNetworkPolicyResponse::Solana(solana_policy.into())
57 }
58 RelayerNetworkPolicy::Stellar(stellar_policy) => {
59 RelayerNetworkPolicyResponse::Stellar(stellar_policy.into())
60 }
61 }
62 }
63}
64
65#[derive(Debug, Serialize, Clone, PartialEq, ToSchema)]
67pub struct RelayerResponse {
68 pub id: String,
69 pub name: String,
70 pub network: String,
71 pub network_type: RelayerNetworkType,
72 pub paused: bool,
73 #[serde(skip_serializing_if = "Option::is_none")]
76 #[schema(nullable = false)]
77 pub policies: Option<RelayerNetworkPolicyResponse>,
78 pub signer_id: String,
79 #[serde(skip_serializing_if = "Option::is_none")]
80 #[schema(nullable = false)]
81 pub notification_id: Option<String>,
82 #[serde(skip_serializing_if = "Option::is_none")]
86 #[schema(nullable = false)]
87 pub custom_rpc_urls: Option<Vec<MaskedRpcConfig>>,
88 #[schema(nullable = false)]
90 pub address: Option<String>,
91 #[schema(nullable = false)]
92 pub system_disabled: Option<bool>,
93 #[serde(skip_serializing_if = "Option::is_none")]
94 #[schema(nullable = false)]
95 pub disabled_reason: Option<DisabledReason>,
96}
97
98#[cfg(test)]
99impl Default for RelayerResponse {
100 fn default() -> Self {
101 Self {
102 id: String::new(),
103 name: String::new(),
104 network: String::new(),
105 network_type: RelayerNetworkType::Evm, paused: false,
107 policies: None,
108 signer_id: String::new(),
109 notification_id: None,
110 custom_rpc_urls: None,
111 address: None,
112 system_disabled: None,
113 disabled_reason: None,
114 }
115 }
116}
117
118#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, ToSchema)]
120#[serde(tag = "network_type")]
121pub enum RelayerStatus {
122 #[serde(rename = "evm")]
123 Evm {
124 balance: String,
125 pending_transactions_count: u64,
126 last_confirmed_transaction_timestamp: Option<String>,
127 system_disabled: bool,
128 paused: bool,
129 nonce: String,
130 },
131 #[serde(rename = "stellar")]
132 Stellar {
133 balance: String,
134 pending_transactions_count: u64,
135 last_confirmed_transaction_timestamp: Option<String>,
136 system_disabled: bool,
137 paused: bool,
138 sequence_number: String,
139 },
140 #[serde(rename = "solana")]
141 Solana {
142 balance: String,
143 pending_transactions_count: u64,
144 last_confirmed_transaction_timestamp: Option<String>,
145 system_disabled: bool,
146 paused: bool,
147 },
148}
149
150fn convert_policy_to_response(
152 policy: RelayerNetworkPolicy,
153 network_type: RelayerNetworkType,
154) -> RelayerNetworkPolicyResponse {
155 match (policy, network_type) {
156 (RelayerNetworkPolicy::Evm(evm_policy), RelayerNetworkType::Evm) => {
157 RelayerNetworkPolicyResponse::Evm(EvmPolicyResponse::from(evm_policy))
158 }
159 (RelayerNetworkPolicy::Solana(solana_policy), RelayerNetworkType::Solana) => {
160 RelayerNetworkPolicyResponse::Solana(SolanaPolicyResponse::from(solana_policy))
161 }
162 (RelayerNetworkPolicy::Stellar(stellar_policy), RelayerNetworkType::Stellar) => {
163 RelayerNetworkPolicyResponse::Stellar(StellarPolicyResponse::from(stellar_policy))
164 }
165 (RelayerNetworkPolicy::Evm(evm_policy), _) => {
167 RelayerNetworkPolicyResponse::Evm(EvmPolicyResponse::from(evm_policy))
168 }
169 (RelayerNetworkPolicy::Solana(solana_policy), _) => {
170 RelayerNetworkPolicyResponse::Solana(SolanaPolicyResponse::from(solana_policy))
171 }
172 (RelayerNetworkPolicy::Stellar(stellar_policy), _) => {
173 RelayerNetworkPolicyResponse::Stellar(StellarPolicyResponse::from(stellar_policy))
174 }
175 }
176}
177
178impl From<Relayer> for RelayerResponse {
179 fn from(relayer: Relayer) -> Self {
180 Self {
181 id: relayer.id.clone(),
182 name: relayer.name.clone(),
183 network: relayer.network.clone(),
184 network_type: relayer.network_type,
185 paused: relayer.paused,
186 policies: relayer
187 .policies
188 .map(|policy| convert_policy_to_response(policy, relayer.network_type)),
189 signer_id: relayer.signer_id,
190 notification_id: relayer.notification_id,
191 custom_rpc_urls: relayer
192 .custom_rpc_urls
193 .map(|urls| urls.into_iter().map(MaskedRpcConfig::from).collect()),
194 address: None,
195 system_disabled: None,
196 disabled_reason: None,
197 }
198 }
199}
200
201impl From<RelayerRepoModel> for RelayerResponse {
202 fn from(model: RelayerRepoModel) -> Self {
203 let policies = if is_empty_policy(&model.policies) {
205 None } else {
207 Some(convert_policy_to_response(
208 model.policies.clone(),
209 model.network_type,
210 ))
211 };
212
213 Self {
214 id: model.id,
215 name: model.name,
216 network: model.network,
217 network_type: model.network_type,
218 paused: model.paused,
219 policies,
220 signer_id: model.signer_id,
221 notification_id: model.notification_id,
222 custom_rpc_urls: model
223 .custom_rpc_urls
224 .map(|urls| urls.into_iter().map(MaskedRpcConfig::from).collect()),
225 address: Some(model.address),
226 system_disabled: Some(model.system_disabled),
227 disabled_reason: model.disabled_reason,
228 }
229 }
230}
231
232impl<'de> serde::Deserialize<'de> for RelayerResponse {
234 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
235 where
236 D: serde::Deserializer<'de>,
237 {
238 use serde::de::Error;
239 use serde_json::Value;
240
241 let value: Value = Value::deserialize(deserializer)?;
243
244 let network_type: RelayerNetworkType = value
246 .get("network_type")
247 .and_then(|v| serde_json::from_value(v.clone()).ok())
248 .ok_or_else(|| D::Error::missing_field("network_type"))?;
249
250 let policies = if let Some(policies_value) = value.get("policies") {
252 if policies_value.is_null() {
253 None
254 } else {
255 let policy_response = match network_type {
257 RelayerNetworkType::Evm => {
258 let evm_policy: EvmPolicyResponse =
259 serde_json::from_value(policies_value.clone())
260 .map_err(D::Error::custom)?;
261 RelayerNetworkPolicyResponse::Evm(evm_policy)
262 }
263 RelayerNetworkType::Solana => {
264 let solana_policy: SolanaPolicyResponse =
265 serde_json::from_value(policies_value.clone())
266 .map_err(D::Error::custom)?;
267 RelayerNetworkPolicyResponse::Solana(solana_policy)
268 }
269 RelayerNetworkType::Stellar => {
270 let stellar_policy: StellarPolicyResponse =
271 serde_json::from_value(policies_value.clone())
272 .map_err(D::Error::custom)?;
273 RelayerNetworkPolicyResponse::Stellar(stellar_policy)
274 }
275 };
276 Some(policy_response)
277 }
278 } else {
279 None
280 };
281
282 Ok(RelayerResponse {
284 id: value
285 .get("id")
286 .and_then(|v| serde_json::from_value(v.clone()).ok())
287 .ok_or_else(|| D::Error::missing_field("id"))?,
288 name: value
289 .get("name")
290 .and_then(|v| serde_json::from_value(v.clone()).ok())
291 .ok_or_else(|| D::Error::missing_field("name"))?,
292 network: value
293 .get("network")
294 .and_then(|v| serde_json::from_value(v.clone()).ok())
295 .ok_or_else(|| D::Error::missing_field("network"))?,
296 network_type,
297 paused: value
298 .get("paused")
299 .and_then(|v| serde_json::from_value(v.clone()).ok())
300 .ok_or_else(|| D::Error::missing_field("paused"))?,
301 policies,
302 signer_id: value
303 .get("signer_id")
304 .and_then(|v| serde_json::from_value(v.clone()).ok())
305 .ok_or_else(|| D::Error::missing_field("signer_id"))?,
306 notification_id: value
307 .get("notification_id")
308 .and_then(|v| serde_json::from_value(v.clone()).ok())
309 .unwrap_or(None),
310 custom_rpc_urls: value
311 .get("custom_rpc_urls")
312 .and_then(|v| serde_json::from_value(v.clone()).ok())
313 .unwrap_or(None),
314 address: value
315 .get("address")
316 .and_then(|v| serde_json::from_value(v.clone()).ok())
317 .unwrap_or(None),
318 system_disabled: value
319 .get("system_disabled")
320 .and_then(|v| serde_json::from_value(v.clone()).ok())
321 .unwrap_or(None),
322 disabled_reason: value
323 .get("disabled_reason")
324 .and_then(|v| serde_json::from_value(v.clone()).ok())
325 .unwrap_or(None),
326 })
327 }
328}
329
330fn is_empty_policy(policy: &RelayerNetworkPolicy) -> bool {
332 match policy {
333 RelayerNetworkPolicy::Evm(evm_policy) => {
334 evm_policy.min_balance.is_none()
335 && evm_policy.gas_limit_estimation.is_none()
336 && evm_policy.gas_price_cap.is_none()
337 && evm_policy.whitelist_receivers.is_none()
338 && evm_policy.eip1559_pricing.is_none()
339 && evm_policy.private_transactions.is_none()
340 && evm_policy.include_revert_data.is_none()
341 }
342 RelayerNetworkPolicy::Solana(solana_policy) => {
343 solana_policy.allowed_programs.is_none()
344 && solana_policy.max_signatures.is_none()
345 && solana_policy.max_tx_data_size.is_none()
346 && solana_policy.min_balance.is_none()
347 && solana_policy.allowed_tokens.is_none()
348 && solana_policy.fee_payment_strategy.is_none()
349 && solana_policy.fee_margin_percentage.is_none()
350 && solana_policy.allowed_accounts.is_none()
351 && solana_policy.disallowed_accounts.is_none()
352 && solana_policy.max_allowed_fee_lamports.is_none()
353 && solana_policy.swap_config.is_none()
354 }
355 RelayerNetworkPolicy::Stellar(stellar_policy) => {
356 stellar_policy.min_balance.is_none()
357 && stellar_policy.max_fee.is_none()
358 && stellar_policy.timeout_seconds.is_none()
359 && stellar_policy.concurrent_transactions.is_none()
360 && stellar_policy.allowed_tokens.is_none()
361 && stellar_policy.fee_payment_strategy.is_none()
362 && stellar_policy.slippage_percentage.is_none()
363 && stellar_policy.fee_margin_percentage.is_none()
364 && stellar_policy.swap_config.is_none()
365 }
366 }
367}
368
369#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, ToSchema)]
371pub struct NetworkPolicyResponse {
372 #[serde(flatten)]
373 pub policy: RelayerNetworkPolicy,
374}
375
376fn default_evm_min_balance() -> u128 {
378 DEFAULT_EVM_MIN_BALANCE
379}
380
381fn default_evm_gas_limit_estimation() -> bool {
382 DEFAULT_EVM_GAS_LIMIT_ESTIMATION
383}
384
385fn default_evm_include_revert_data() -> bool {
386 DEFAULT_EVM_INCLUDE_REVERT_DATA
387}
388
389fn default_solana_min_balance() -> u64 {
391 DEFAULT_SOLANA_MIN_BALANCE
392}
393
394fn default_stellar_min_balance() -> u64 {
396 DEFAULT_STELLAR_MIN_BALANCE
397}
398
399fn default_solana_max_tx_data_size() -> u16 {
401 DEFAULT_SOLANA_MAX_TX_DATA_SIZE
402}
403#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, ToSchema)]
405#[serde(deny_unknown_fields)]
406pub struct EvmPolicyResponse {
407 #[serde(
408 default = "default_evm_min_balance",
409 serialize_with = "crate::utils::serialize_u128_as_number",
410 deserialize_with = "crate::utils::deserialize_u128_as_number"
411 )]
412 #[schema(nullable = false)]
413 pub min_balance: u128,
414 #[serde(default = "default_evm_gas_limit_estimation")]
415 #[schema(nullable = false)]
416 pub gas_limit_estimation: bool,
417 #[serde(
418 skip_serializing_if = "Option::is_none",
419 serialize_with = "crate::utils::serialize_optional_u128_as_number",
420 deserialize_with = "crate::utils::deserialize_optional_u128_as_number",
421 default
422 )]
423 #[schema(nullable = false)]
424 pub gas_price_cap: Option<u128>,
425 #[serde(skip_serializing_if = "Option::is_none")]
426 #[schema(nullable = false)]
427 pub whitelist_receivers: Option<Vec<String>>,
428 #[serde(skip_serializing_if = "Option::is_none")]
429 #[schema(nullable = false)]
430 pub eip1559_pricing: Option<bool>,
431 #[serde(skip_serializing_if = "Option::is_none")]
432 #[schema(nullable = false)]
433 pub private_transactions: Option<bool>,
434 #[serde(default = "default_evm_include_revert_data")]
435 #[schema(nullable = false)]
436 pub include_revert_data: bool,
437}
438
439#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, ToSchema)]
441#[serde(deny_unknown_fields)]
442pub struct SolanaPolicyResponse {
443 #[serde(skip_serializing_if = "Option::is_none")]
444 #[schema(nullable = false)]
445 pub allowed_programs: Option<Vec<String>>,
446 #[serde(skip_serializing_if = "Option::is_none")]
447 #[schema(nullable = false)]
448 pub max_signatures: Option<u8>,
449 #[schema(nullable = false)]
450 #[serde(default = "default_solana_max_tx_data_size")]
451 pub max_tx_data_size: u16,
452 #[serde(default = "default_solana_min_balance")]
453 #[schema(nullable = false)]
454 pub min_balance: u64,
455 #[serde(skip_serializing_if = "Option::is_none")]
456 #[schema(nullable = false)]
457 pub allowed_tokens: Option<Vec<SolanaAllowedTokensPolicy>>,
458 #[serde(skip_serializing_if = "Option::is_none")]
459 #[schema(nullable = false)]
460 pub fee_payment_strategy: Option<SolanaFeePaymentStrategy>,
461 #[serde(skip_serializing_if = "Option::is_none")]
462 #[schema(nullable = false)]
463 pub fee_margin_percentage: Option<f32>,
464 #[serde(skip_serializing_if = "Option::is_none")]
465 #[schema(nullable = false)]
466 pub allowed_accounts: Option<Vec<String>>,
467 #[serde(skip_serializing_if = "Option::is_none")]
468 #[schema(nullable = false)]
469 pub disallowed_accounts: Option<Vec<String>>,
470 #[serde(skip_serializing_if = "Option::is_none")]
471 #[schema(nullable = false)]
472 pub max_allowed_fee_lamports: Option<u64>,
473 #[serde(skip_serializing_if = "Option::is_none")]
474 #[schema(nullable = false)]
475 pub swap_config: Option<RelayerSolanaSwapConfig>,
476}
477
478#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, ToSchema)]
480#[serde(deny_unknown_fields)]
481pub struct StellarPolicyResponse {
482 #[serde(skip_serializing_if = "Option::is_none")]
483 #[schema(nullable = false)]
484 pub max_fee: Option<u32>,
485 #[serde(skip_serializing_if = "Option::is_none")]
486 #[schema(nullable = false)]
487 pub timeout_seconds: Option<u64>,
488 #[serde(default = "default_stellar_min_balance")]
489 #[schema(nullable = false)]
490 pub min_balance: u64,
491 #[serde(skip_serializing_if = "Option::is_none")]
492 #[schema(nullable = false)]
493 pub concurrent_transactions: Option<bool>,
494 #[serde(skip_serializing_if = "Option::is_none")]
495 #[schema(nullable = false)]
496 pub allowed_tokens: Option<Vec<StellarAllowedTokensPolicy>>,
497 #[serde(skip_serializing_if = "Option::is_none")]
498 #[schema(nullable = false)]
499 pub fee_payment_strategy: Option<StellarFeePaymentStrategy>,
500 #[serde(skip_serializing_if = "Option::is_none")]
501 #[schema(nullable = false)]
502 pub slippage_percentage: Option<f32>,
503 #[serde(skip_serializing_if = "Option::is_none")]
504 #[schema(nullable = false)]
505 pub fee_margin_percentage: Option<f32>,
506 #[serde(skip_serializing_if = "Option::is_none")]
507 #[schema(nullable = false)]
508 pub swap_config: Option<RelayerStellarSwapConfig>,
509}
510
511impl From<RelayerEvmPolicy> for EvmPolicyResponse {
512 fn from(policy: RelayerEvmPolicy) -> Self {
513 Self {
514 min_balance: policy.min_balance.unwrap_or(DEFAULT_EVM_MIN_BALANCE),
515 gas_limit_estimation: policy
516 .gas_limit_estimation
517 .unwrap_or(DEFAULT_EVM_GAS_LIMIT_ESTIMATION),
518 gas_price_cap: policy.gas_price_cap,
519 whitelist_receivers: policy.whitelist_receivers,
520 eip1559_pricing: policy.eip1559_pricing,
521 private_transactions: policy.private_transactions,
522 include_revert_data: policy
523 .include_revert_data
524 .unwrap_or(DEFAULT_EVM_INCLUDE_REVERT_DATA),
525 }
526 }
527}
528
529impl From<RelayerSolanaPolicy> for SolanaPolicyResponse {
530 fn from(policy: RelayerSolanaPolicy) -> Self {
531 Self {
532 allowed_programs: policy.allowed_programs,
533 max_signatures: policy.max_signatures,
534 max_tx_data_size: policy
535 .max_tx_data_size
536 .unwrap_or(DEFAULT_SOLANA_MAX_TX_DATA_SIZE),
537 min_balance: policy.min_balance.unwrap_or(DEFAULT_SOLANA_MIN_BALANCE),
538 allowed_tokens: policy.allowed_tokens,
539 fee_payment_strategy: policy.fee_payment_strategy,
540 fee_margin_percentage: policy.fee_margin_percentage,
541 allowed_accounts: policy.allowed_accounts,
542 disallowed_accounts: policy.disallowed_accounts,
543 max_allowed_fee_lamports: policy.max_allowed_fee_lamports,
544 swap_config: policy.swap_config,
545 }
546 }
547}
548
549impl From<RelayerStellarPolicy> for StellarPolicyResponse {
550 fn from(policy: RelayerStellarPolicy) -> Self {
551 Self {
552 min_balance: policy.min_balance.unwrap_or(DEFAULT_STELLAR_MIN_BALANCE),
553 max_fee: policy.max_fee,
554 timeout_seconds: policy.timeout_seconds,
555 concurrent_transactions: policy.concurrent_transactions,
556 allowed_tokens: policy.allowed_tokens,
557 fee_payment_strategy: policy.fee_payment_strategy,
558 slippage_percentage: policy.slippage_percentage,
559 fee_margin_percentage: policy.fee_margin_percentage,
560 swap_config: policy.swap_config,
561 }
562 }
563}
564
565#[cfg(test)]
566mod tests {
567 use super::*;
568 use crate::models::{
569 relayer::{
570 RelayerEvmPolicy, RelayerSolanaPolicy, RelayerSolanaSwapConfig, RelayerStellarPolicy,
571 SolanaAllowedTokensPolicy, SolanaFeePaymentStrategy, SolanaSwapStrategy,
572 StellarAllowedTokensPolicy, StellarFeePaymentStrategy, StellarSwapStrategy,
573 },
574 StellarTokenKind, StellarTokenMetadata,
575 };
576
577 #[test]
578 fn test_from_domain_relayer() {
579 let relayer = Relayer::new(
580 "test-relayer".to_string(),
581 "Test Relayer".to_string(),
582 "mainnet".to_string(),
583 false,
584 RelayerNetworkType::Evm,
585 Some(RelayerNetworkPolicy::Evm(RelayerEvmPolicy {
586 include_revert_data: None,
587 gas_price_cap: Some(100_000_000_000),
588 whitelist_receivers: None,
589 eip1559_pricing: Some(true),
590 private_transactions: None,
591 min_balance: None,
592 gas_limit_estimation: None,
593 })),
594 "test-signer".to_string(),
595 None,
596 None,
597 );
598
599 let response: RelayerResponse = relayer.clone().into();
600
601 assert_eq!(response.id, relayer.id);
602 assert_eq!(response.name, relayer.name);
603 assert_eq!(response.network, relayer.network);
604 assert_eq!(response.network_type, relayer.network_type);
605 assert_eq!(response.paused, relayer.paused);
606 assert_eq!(
607 response.policies,
608 Some(RelayerNetworkPolicyResponse::Evm(
609 RelayerEvmPolicy {
610 include_revert_data: None,
611 gas_price_cap: Some(100_000_000_000),
612 whitelist_receivers: None,
613 eip1559_pricing: Some(true),
614 private_transactions: None,
615 min_balance: Some(DEFAULT_EVM_MIN_BALANCE),
616 gas_limit_estimation: Some(DEFAULT_EVM_GAS_LIMIT_ESTIMATION),
617 }
618 .into()
619 ))
620 );
621 assert_eq!(response.signer_id, relayer.signer_id);
622 assert_eq!(response.notification_id, relayer.notification_id);
623 assert_eq!(response.custom_rpc_urls, None);
625 assert_eq!(response.address, None);
626 assert_eq!(response.system_disabled, None);
627 }
628
629 #[test]
630 fn test_from_domain_relayer_solana() {
631 let relayer = Relayer::new(
632 "test-solana-relayer".to_string(),
633 "Test Solana Relayer".to_string(),
634 "mainnet".to_string(),
635 false,
636 RelayerNetworkType::Solana,
637 Some(RelayerNetworkPolicy::Solana(RelayerSolanaPolicy {
638 allowed_programs: Some(vec!["11111111111111111111111111111111".to_string()]),
639 max_signatures: Some(5),
640 min_balance: Some(1000000),
641 fee_payment_strategy: Some(SolanaFeePaymentStrategy::Relayer),
642 allowed_tokens: Some(vec![SolanaAllowedTokensPolicy::new(
643 "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v".to_string(),
644 Some(100000),
645 None,
646 )]),
647 max_tx_data_size: None,
648 fee_margin_percentage: None,
649 allowed_accounts: None,
650 disallowed_accounts: None,
651 max_allowed_fee_lamports: None,
652 swap_config: None,
653 })),
654 "test-signer".to_string(),
655 None,
656 None,
657 );
658
659 let response: RelayerResponse = relayer.clone().into();
660
661 assert_eq!(response.id, relayer.id);
662 assert_eq!(response.network_type, RelayerNetworkType::Solana);
663 assert!(response.policies.is_some());
664
665 if let Some(RelayerNetworkPolicyResponse::Solana(solana_response)) = response.policies {
666 assert_eq!(solana_response.min_balance, 1000000);
667 assert_eq!(solana_response.max_signatures, Some(5));
668 } else {
669 panic!("Expected Solana policy response");
670 }
671 }
672
673 #[test]
674 fn test_from_domain_relayer_stellar() {
675 let relayer = Relayer::new(
676 "test-stellar-relayer".to_string(),
677 "Test Stellar Relayer".to_string(),
678 "mainnet".to_string(),
679 false,
680 RelayerNetworkType::Stellar,
681 Some(RelayerNetworkPolicy::Stellar(RelayerStellarPolicy {
682 min_balance: Some(20000000),
683 max_fee: Some(100000),
684 timeout_seconds: Some(30),
685 concurrent_transactions: None,
686 allowed_tokens: None,
687 fee_payment_strategy: Some(StellarFeePaymentStrategy::Relayer),
688 slippage_percentage: None,
689 fee_margin_percentage: None,
690 swap_config: None,
691 })),
692 "test-signer".to_string(),
693 None,
694 None,
695 );
696
697 let response: RelayerResponse = relayer.clone().into();
698
699 assert_eq!(response.id, relayer.id);
700 assert_eq!(response.network_type, RelayerNetworkType::Stellar);
701 assert!(response.policies.is_some());
702
703 if let Some(RelayerNetworkPolicyResponse::Stellar(stellar_response)) = response.policies {
704 assert_eq!(stellar_response.min_balance, 20000000);
705 } else {
706 panic!("Expected Stellar policy response");
707 }
708 }
709
710 #[test]
711 fn test_response_serialization() {
712 let response = RelayerResponse {
713 id: "test-relayer".to_string(),
714 name: "Test Relayer".to_string(),
715 network: "mainnet".to_string(),
716 network_type: RelayerNetworkType::Evm,
717 paused: false,
718 policies: Some(RelayerNetworkPolicyResponse::Evm(EvmPolicyResponse {
719 include_revert_data: DEFAULT_EVM_INCLUDE_REVERT_DATA,
720 gas_price_cap: Some(50000000000),
721 whitelist_receivers: None,
722 eip1559_pricing: Some(true),
723 private_transactions: None,
724 min_balance: DEFAULT_EVM_MIN_BALANCE,
725 gas_limit_estimation: DEFAULT_EVM_GAS_LIMIT_ESTIMATION,
726 })),
727 signer_id: "test-signer".to_string(),
728 notification_id: None,
729 custom_rpc_urls: None,
730 address: Some("0x123...".to_string()),
731 system_disabled: Some(false),
732 ..Default::default()
733 };
734
735 let serialized = serde_json::to_string(&response).unwrap();
737 assert!(!serialized.is_empty());
738
739 let deserialized: RelayerResponse = serde_json::from_str(&serialized).unwrap();
741 assert_eq!(response.id, deserialized.id);
742 assert_eq!(response.name, deserialized.name);
743 }
744
745 #[test]
746 fn test_solana_response_serialization() {
747 let response = RelayerResponse {
748 id: "test-solana-relayer".to_string(),
749 name: "Test Solana Relayer".to_string(),
750 network: "mainnet".to_string(),
751 network_type: RelayerNetworkType::Solana,
752 paused: false,
753 policies: Some(RelayerNetworkPolicyResponse::Solana(SolanaPolicyResponse {
754 allowed_programs: Some(vec!["11111111111111111111111111111111".to_string()]),
755 max_signatures: Some(5),
756 max_tx_data_size: DEFAULT_SOLANA_MAX_TX_DATA_SIZE,
757 min_balance: 1000000,
758 allowed_tokens: Some(vec![SolanaAllowedTokensPolicy::new(
759 "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v".to_string(),
760 Some(100000),
761 None,
762 )]),
763 fee_payment_strategy: Some(SolanaFeePaymentStrategy::Relayer),
764 fee_margin_percentage: Some(5.0),
765 allowed_accounts: None,
766 disallowed_accounts: None,
767 max_allowed_fee_lamports: Some(500000),
768 swap_config: Some(RelayerSolanaSwapConfig {
769 strategy: Some(SolanaSwapStrategy::JupiterSwap),
770 cron_schedule: Some("0 0 * * *".to_string()),
771 min_balance_threshold: Some(500000),
772 jupiter_swap_options: None,
773 }),
774 })),
775 signer_id: "test-signer".to_string(),
776 notification_id: None,
777 custom_rpc_urls: None,
778 address: Some("SolanaAddress123...".to_string()),
779 system_disabled: Some(false),
780 ..Default::default()
781 };
782
783 let serialized = serde_json::to_string(&response).unwrap();
785 assert!(!serialized.is_empty());
786
787 let deserialized: RelayerResponse = serde_json::from_str(&serialized).unwrap();
789 assert_eq!(response.id, deserialized.id);
790 assert_eq!(response.network_type, RelayerNetworkType::Solana);
791 }
792
793 #[test]
794 fn test_stellar_response_serialization() {
795 let response = RelayerResponse {
796 id: "test-stellar-relayer".to_string(),
797 name: "Test Stellar Relayer".to_string(),
798 network: "mainnet".to_string(),
799 network_type: RelayerNetworkType::Stellar,
800 paused: false,
801 policies: Some(RelayerNetworkPolicyResponse::Stellar(
802 StellarPolicyResponse {
803 max_fee: Some(5000),
804 timeout_seconds: None,
805 min_balance: 20000000,
806 concurrent_transactions: None,
807 allowed_tokens: None,
808 fee_payment_strategy: None,
809 slippage_percentage: None,
810 fee_margin_percentage: None,
811 swap_config: None,
812 },
813 )),
814 signer_id: "test-signer".to_string(),
815 notification_id: None,
816 custom_rpc_urls: None,
817 address: Some("GXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX".to_string()),
818 system_disabled: Some(false),
819 ..Default::default()
820 };
821
822 let serialized = serde_json::to_string(&response).unwrap();
824 assert!(!serialized.is_empty());
825
826 let deserialized: RelayerResponse = serde_json::from_str(&serialized).unwrap();
828 assert_eq!(response.id, deserialized.id);
829 assert_eq!(response.network_type, RelayerNetworkType::Stellar);
830
831 if let Some(RelayerNetworkPolicyResponse::Stellar(stellar_policy)) = deserialized.policies {
833 assert_eq!(stellar_policy.min_balance, 20000000);
834 assert_eq!(stellar_policy.max_fee, Some(5000));
835 assert_eq!(stellar_policy.timeout_seconds, None);
836 } else {
837 panic!("Expected Stellar policy in deserialized response");
838 }
839 }
840
841 #[test]
842 fn test_response_without_redundant_network_type() {
843 let response = RelayerResponse {
844 id: "test-relayer".to_string(),
845 name: "Test Relayer".to_string(),
846 network: "mainnet".to_string(),
847 network_type: RelayerNetworkType::Evm,
848 paused: false,
849 policies: Some(RelayerNetworkPolicyResponse::Evm(EvmPolicyResponse {
850 include_revert_data: DEFAULT_EVM_INCLUDE_REVERT_DATA,
851 gas_price_cap: Some(100_000_000_000),
852 whitelist_receivers: None,
853 eip1559_pricing: Some(true),
854 private_transactions: None,
855 min_balance: DEFAULT_EVM_MIN_BALANCE,
856 gas_limit_estimation: DEFAULT_EVM_GAS_LIMIT_ESTIMATION,
857 })),
858 signer_id: "test-signer".to_string(),
859 notification_id: None,
860 custom_rpc_urls: None,
861 address: Some("0x123...".to_string()),
862 system_disabled: Some(false),
863 ..Default::default()
864 };
865
866 let serialized = serde_json::to_string_pretty(&response).unwrap();
867
868 assert!(serialized.contains(r#""network_type": "evm""#));
869
870 let network_type_count = serialized.matches(r#""network_type""#).count();
872 assert_eq!(
873 network_type_count, 1,
874 "Should only have one network_type field at top level, not in policies"
875 );
876
877 assert!(serialized.contains(r#""gas_price_cap": 100000000000"#));
878 assert!(serialized.contains(r#""eip1559_pricing": true"#));
879 }
880
881 #[test]
882 fn test_solana_response_without_redundant_network_type() {
883 let response = RelayerResponse {
884 id: "test-solana-relayer".to_string(),
885 name: "Test Solana Relayer".to_string(),
886 network: "mainnet".to_string(),
887 network_type: RelayerNetworkType::Solana,
888 paused: false,
889 policies: Some(RelayerNetworkPolicyResponse::Solana(SolanaPolicyResponse {
890 allowed_programs: Some(vec!["11111111111111111111111111111111".to_string()]),
891 max_signatures: Some(5),
892 max_tx_data_size: DEFAULT_SOLANA_MAX_TX_DATA_SIZE,
893 min_balance: 1000000,
894 allowed_tokens: None,
895 fee_payment_strategy: Some(SolanaFeePaymentStrategy::Relayer),
896 fee_margin_percentage: None,
897 allowed_accounts: None,
898 disallowed_accounts: None,
899 max_allowed_fee_lamports: None,
900 swap_config: None,
901 })),
902 signer_id: "test-signer".to_string(),
903 notification_id: None,
904 custom_rpc_urls: None,
905 address: Some("SolanaAddress123...".to_string()),
906 system_disabled: Some(false),
907 ..Default::default()
908 };
909
910 let serialized = serde_json::to_string_pretty(&response).unwrap();
911
912 assert!(serialized.contains(r#""network_type": "solana""#));
913
914 let network_type_count = serialized.matches(r#""network_type""#).count();
916 assert_eq!(
917 network_type_count, 1,
918 "Should only have one network_type field at top level, not in policies"
919 );
920
921 assert!(serialized.contains(r#""max_signatures": 5"#));
922 assert!(serialized.contains(r#""fee_payment_strategy": "relayer""#));
923 }
924
925 #[test]
926 fn test_stellar_response_without_redundant_network_type() {
927 let response = RelayerResponse {
928 id: "test-stellar-relayer".to_string(),
929 name: "Test Stellar Relayer".to_string(),
930 network: "mainnet".to_string(),
931 network_type: RelayerNetworkType::Stellar,
932 paused: false,
933 policies: Some(RelayerNetworkPolicyResponse::Stellar(
934 StellarPolicyResponse {
935 min_balance: 20000000,
936 max_fee: Some(100000),
937 timeout_seconds: Some(30),
938 concurrent_transactions: None,
939 allowed_tokens: None,
940 fee_payment_strategy: None,
941 slippage_percentage: None,
942 fee_margin_percentage: None,
943 swap_config: None,
944 },
945 )),
946 signer_id: "test-signer".to_string(),
947 notification_id: None,
948 custom_rpc_urls: None,
949 address: Some("GXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX".to_string()),
950 system_disabled: Some(false),
951 ..Default::default()
952 };
953
954 let serialized = serde_json::to_string_pretty(&response).unwrap();
955
956 assert!(serialized.contains(r#""network_type": "stellar""#));
957
958 let network_type_count = serialized.matches(r#""network_type""#).count();
960 assert_eq!(
961 network_type_count, 1,
962 "Should only have one network_type field at top level, not in policies"
963 );
964
965 assert!(serialized.contains(r#""min_balance": 20000000"#));
966 assert!(serialized.contains(r#""max_fee": 100000"#));
967 assert!(serialized.contains(r#""timeout_seconds": 30"#));
968 }
969
970 #[test]
971 fn test_empty_policies_not_returned_in_response() {
972 let repo_model = RelayerRepoModel {
974 id: "test-relayer".to_string(),
975 name: "Test Relayer".to_string(),
976 network: "mainnet".to_string(),
977 network_type: RelayerNetworkType::Evm,
978 paused: false,
979 policies: RelayerNetworkPolicy::Evm(RelayerEvmPolicy::default()), signer_id: "test-signer".to_string(),
981 notification_id: None,
982 custom_rpc_urls: None,
983 address: "0x123...".to_string(),
984 system_disabled: false,
985 ..Default::default()
986 };
987
988 let response = RelayerResponse::from(repo_model);
990
991 assert_eq!(response.policies, None);
993
994 let serialized = serde_json::to_string(&response).unwrap();
996 assert!(
997 !serialized.contains("policies"),
998 "Empty policies should not appear in JSON response"
999 );
1000 }
1001
1002 #[test]
1003 fn test_empty_solana_policies_not_returned_in_response() {
1004 let repo_model = RelayerRepoModel {
1006 id: "test-solana-relayer".to_string(),
1007 name: "Test Solana Relayer".to_string(),
1008 network: "mainnet".to_string(),
1009 network_type: RelayerNetworkType::Solana,
1010 paused: false,
1011 policies: RelayerNetworkPolicy::Solana(RelayerSolanaPolicy::default()), signer_id: "test-signer".to_string(),
1013 notification_id: None,
1014 custom_rpc_urls: None,
1015 address: "SolanaAddress123...".to_string(),
1016 system_disabled: false,
1017 ..Default::default()
1018 };
1019
1020 let response = RelayerResponse::from(repo_model);
1022
1023 assert_eq!(response.policies, None);
1025
1026 let serialized = serde_json::to_string(&response).unwrap();
1028 assert!(
1029 !serialized.contains("policies"),
1030 "Empty Solana policies should not appear in JSON response"
1031 );
1032 }
1033
1034 #[test]
1035 fn test_empty_stellar_policies_not_returned_in_response() {
1036 let repo_model = RelayerRepoModel {
1038 id: "test-stellar-relayer".to_string(),
1039 name: "Test Stellar Relayer".to_string(),
1040 network: "mainnet".to_string(),
1041 network_type: RelayerNetworkType::Stellar,
1042 paused: false,
1043 policies: RelayerNetworkPolicy::Stellar(RelayerStellarPolicy::default()), signer_id: "test-signer".to_string(),
1045 notification_id: None,
1046 custom_rpc_urls: None,
1047 address: "GXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX".to_string(),
1048 system_disabled: false,
1049 ..Default::default()
1050 };
1051
1052 let response = RelayerResponse::from(repo_model);
1054
1055 assert_eq!(response.policies, None);
1057
1058 let serialized = serde_json::to_string(&response).unwrap();
1060 assert!(
1061 !serialized.contains("policies"),
1062 "Empty Stellar policies should not appear in JSON response"
1063 );
1064 }
1065
1066 #[test]
1067 fn test_user_provided_policies_returned_in_response() {
1068 let repo_model = RelayerRepoModel {
1070 id: "test-relayer".to_string(),
1071 name: "Test Relayer".to_string(),
1072 network: "mainnet".to_string(),
1073 network_type: RelayerNetworkType::Evm,
1074 paused: false,
1075 policies: RelayerNetworkPolicy::Evm(RelayerEvmPolicy {
1076 include_revert_data: None,
1077 gas_price_cap: Some(100_000_000_000),
1078 eip1559_pricing: Some(true),
1079 min_balance: None, gas_limit_estimation: None,
1081 whitelist_receivers: None,
1082 private_transactions: None,
1083 }),
1084 signer_id: "test-signer".to_string(),
1085 notification_id: None,
1086 custom_rpc_urls: None,
1087 address: "0x123...".to_string(),
1088 system_disabled: false,
1089 ..Default::default()
1090 };
1091
1092 let response = RelayerResponse::from(repo_model);
1094
1095 assert!(response.policies.is_some());
1097
1098 let serialized = serde_json::to_string(&response).unwrap();
1100 assert!(
1101 serialized.contains("policies"),
1102 "User-provided policies should appear in JSON response"
1103 );
1104 assert!(
1105 serialized.contains("gas_price_cap"),
1106 "User-provided policy values should appear in JSON response"
1107 );
1108 }
1109
1110 #[test]
1111 fn test_user_provided_solana_policies_returned_in_response() {
1112 let repo_model = RelayerRepoModel {
1114 id: "test-solana-relayer".to_string(),
1115 name: "Test Solana Relayer".to_string(),
1116 network: "mainnet".to_string(),
1117 network_type: RelayerNetworkType::Solana,
1118 paused: false,
1119 policies: RelayerNetworkPolicy::Solana(RelayerSolanaPolicy {
1120 max_signatures: Some(5),
1121 fee_payment_strategy: Some(SolanaFeePaymentStrategy::Relayer),
1122 min_balance: Some(1000000),
1123 allowed_programs: None, max_tx_data_size: None,
1125 allowed_tokens: None,
1126 fee_margin_percentage: None,
1127 allowed_accounts: None,
1128 disallowed_accounts: None,
1129 max_allowed_fee_lamports: None,
1130 swap_config: None,
1131 }),
1132 signer_id: "test-signer".to_string(),
1133 notification_id: None,
1134 custom_rpc_urls: None,
1135 address: "SolanaAddress123...".to_string(),
1136 system_disabled: false,
1137 ..Default::default()
1138 };
1139
1140 let response = RelayerResponse::from(repo_model);
1142
1143 assert!(response.policies.is_some());
1145
1146 let serialized = serde_json::to_string(&response).unwrap();
1148 assert!(
1149 serialized.contains("policies"),
1150 "User-provided Solana policies should appear in JSON response"
1151 );
1152 assert!(
1153 serialized.contains("max_signatures"),
1154 "User-provided Solana policy values should appear in JSON response"
1155 );
1156 assert!(
1157 serialized.contains("fee_payment_strategy"),
1158 "User-provided Solana policy values should appear in JSON response"
1159 );
1160 }
1161
1162 #[test]
1163 fn test_user_provided_stellar_policies_returned_in_response() {
1164 let repo_model = RelayerRepoModel {
1166 id: "test-stellar-relayer".to_string(),
1167 name: "Test Stellar Relayer".to_string(),
1168 network: "mainnet".to_string(),
1169 network_type: RelayerNetworkType::Stellar,
1170 paused: false,
1171 policies: RelayerNetworkPolicy::Stellar(RelayerStellarPolicy {
1172 max_fee: Some(100000),
1173 timeout_seconds: Some(30),
1174 min_balance: Some(20000000),
1175 concurrent_transactions: Some(true),
1176 allowed_tokens: Some(vec![StellarAllowedTokensPolicy::new(
1177 "USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN".to_string(),
1178 Some(StellarTokenMetadata {
1179 kind: StellarTokenKind::Classic {
1180 code: "USDC".to_string(),
1181 issuer: "GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN"
1182 .to_string(),
1183 },
1184 decimals: 6,
1185 canonical_asset_id:
1186 "USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN"
1187 .to_string(),
1188 }),
1189 None,
1190 None,
1191 )]),
1192 fee_payment_strategy: Some(StellarFeePaymentStrategy::Relayer),
1193 slippage_percentage: Some(0.5),
1194 fee_margin_percentage: Some(2.0),
1195 swap_config: Some(RelayerStellarSwapConfig {
1196 strategies: vec![StellarSwapStrategy::Soroswap],
1197 cron_schedule: Some("0 0 * * *".to_string()),
1198 min_balance_threshold: Some(10000000),
1199 }),
1200 }),
1201 signer_id: "test-signer".to_string(),
1202 notification_id: None,
1203 custom_rpc_urls: None,
1204 address: "GXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX".to_string(),
1205 system_disabled: false,
1206 ..Default::default()
1207 };
1208
1209 let response = RelayerResponse::from(repo_model);
1211
1212 assert!(response.policies.is_some());
1214
1215 let serialized = serde_json::to_string(&response).unwrap();
1217 assert!(
1218 serialized.contains("policies"),
1219 "User-provided Stellar policies should appear in JSON response"
1220 );
1221 assert!(
1222 serialized.contains("max_fee"),
1223 "User-provided Stellar policy values should appear in JSON response"
1224 );
1225 assert!(
1226 serialized.contains("timeout_seconds"),
1227 "User-provided Stellar policy values should appear in JSON response"
1228 );
1229 assert!(
1230 serialized.contains("allowed_tokens"),
1231 "User-provided Stellar policy values should appear in JSON response"
1232 );
1233 assert!(
1234 serialized.contains("fee_payment_strategy"),
1235 "User-provided Stellar policy values should appear in JSON response"
1236 );
1237 assert!(
1238 serialized.contains("slippage_percentage"),
1239 "User-provided Stellar policy values should appear in JSON response"
1240 );
1241 assert!(
1242 serialized.contains("fee_margin_percentage"),
1243 "User-provided Stellar policy values should appear in JSON response"
1244 );
1245 assert!(
1246 serialized.contains("swap_config"),
1247 "User-provided Stellar policy values should appear in JSON response"
1248 );
1249 }
1250
1251 #[test]
1252 fn test_stellar_fee_payment_strategy_explicitly_set_vs_omitted() {
1253 let policy_with_user = RelayerStellarPolicy {
1255 min_balance: Some(20000000),
1256 max_fee: Some(100000),
1257 timeout_seconds: Some(30),
1258 concurrent_transactions: None,
1259 allowed_tokens: None,
1260 fee_payment_strategy: Some(StellarFeePaymentStrategy::User),
1261 slippage_percentage: None,
1262 fee_margin_percentage: None,
1263 swap_config: None,
1264 };
1265
1266 let response_with_user = StellarPolicyResponse::from(policy_with_user);
1267 let serialized_with_user = serde_json::to_string(&response_with_user).unwrap();
1268 assert!(
1269 serialized_with_user.contains(r#""fee_payment_strategy":"user""#),
1270 "Explicitly set User fee_payment_strategy should appear in JSON response"
1271 );
1272
1273 let policy_with_relayer = RelayerStellarPolicy {
1275 min_balance: Some(20000000),
1276 max_fee: Some(100000),
1277 timeout_seconds: Some(30),
1278 concurrent_transactions: None,
1279 allowed_tokens: None,
1280 fee_payment_strategy: Some(StellarFeePaymentStrategy::Relayer),
1281 slippage_percentage: None,
1282 fee_margin_percentage: None,
1283 swap_config: None,
1284 };
1285
1286 let response_with_relayer = StellarPolicyResponse::from(policy_with_relayer);
1287 let serialized_with_relayer = serde_json::to_string(&response_with_relayer).unwrap();
1288 assert!(
1289 serialized_with_relayer.contains(r#""fee_payment_strategy":"relayer""#),
1290 "Explicitly set Relayer fee_payment_strategy should appear in JSON response"
1291 );
1292
1293 let policy_omitted = RelayerStellarPolicy {
1295 min_balance: Some(20000000),
1296 max_fee: Some(100000),
1297 timeout_seconds: Some(30),
1298 concurrent_transactions: None,
1299 allowed_tokens: None,
1300 fee_payment_strategy: None,
1301 slippage_percentage: None,
1302 fee_margin_percentage: None,
1303 swap_config: None,
1304 };
1305
1306 let response_omitted = StellarPolicyResponse::from(policy_omitted);
1307 let serialized_omitted = serde_json::to_string(&response_omitted).unwrap();
1308 assert!(
1309 !serialized_omitted.contains("fee_payment_strategy"),
1310 "Omitted fee_payment_strategy (None) should NOT appear in JSON response"
1311 );
1312
1313 let empty_policy = RelayerStellarPolicy::default();
1315 assert!(
1316 is_empty_policy(&RelayerNetworkPolicy::Stellar(empty_policy)),
1317 "Policy with all None values should be considered empty"
1318 );
1319
1320 let policy_with_user_only = RelayerStellarPolicy {
1321 fee_payment_strategy: Some(StellarFeePaymentStrategy::User),
1322 ..Default::default()
1323 };
1324 assert!(
1325 !is_empty_policy(&RelayerNetworkPolicy::Stellar(policy_with_user_only)),
1326 "Policy with explicitly set User fee_payment_strategy should NOT be considered empty"
1327 );
1328
1329 let policy_with_relayer_only = RelayerStellarPolicy {
1330 fee_payment_strategy: Some(StellarFeePaymentStrategy::Relayer),
1331 ..Default::default()
1332 };
1333 assert!(
1334 !is_empty_policy(&RelayerNetworkPolicy::Stellar(policy_with_relayer_only)),
1335 "Policy with explicitly set Relayer fee_payment_strategy should NOT be considered empty"
1336 );
1337 }
1338
1339 #[test]
1340 fn test_relayer_status_serialization() {
1341 let evm_status = RelayerStatus::Evm {
1343 balance: "1000000000000000000".to_string(),
1344 pending_transactions_count: 5,
1345 last_confirmed_transaction_timestamp: Some("2024-01-01T00:00:00Z".to_string()),
1346 system_disabled: false,
1347 paused: false,
1348 nonce: "42".to_string(),
1349 };
1350
1351 let serialized = serde_json::to_string(&evm_status).unwrap();
1352 assert!(serialized.contains(r#""network_type":"evm""#));
1353 assert!(serialized.contains(r#""nonce":"42""#));
1354 assert!(serialized.contains(r#""balance":"1000000000000000000""#));
1355
1356 let solana_status = RelayerStatus::Solana {
1358 balance: "5000000000".to_string(),
1359 pending_transactions_count: 3,
1360 last_confirmed_transaction_timestamp: None,
1361 system_disabled: false,
1362 paused: true,
1363 };
1364
1365 let serialized = serde_json::to_string(&solana_status).unwrap();
1366 assert!(serialized.contains(r#""network_type":"solana""#));
1367 assert!(serialized.contains(r#""balance":"5000000000""#));
1368 assert!(serialized.contains(r#""paused":true"#));
1369
1370 let stellar_status = RelayerStatus::Stellar {
1372 balance: "1000000000".to_string(),
1373 pending_transactions_count: 2,
1374 last_confirmed_transaction_timestamp: Some("2024-01-01T12:00:00Z".to_string()),
1375 system_disabled: true,
1376 paused: false,
1377 sequence_number: "123456789".to_string(),
1378 };
1379
1380 let serialized = serde_json::to_string(&stellar_status).unwrap();
1381 assert!(serialized.contains(r#""network_type":"stellar""#));
1382 assert!(serialized.contains(r#""sequence_number":"123456789""#));
1383 assert!(serialized.contains(r#""system_disabled":true"#));
1384 }
1385
1386 #[test]
1387 fn test_relayer_status_deserialization() {
1388 let evm_json = r#"{
1390 "network_type": "evm",
1391 "balance": "1000000000000000000",
1392 "pending_transactions_count": 5,
1393 "last_confirmed_transaction_timestamp": "2024-01-01T00:00:00Z",
1394 "system_disabled": false,
1395 "paused": false,
1396 "nonce": "42"
1397 }"#;
1398
1399 let status: RelayerStatus = serde_json::from_str(evm_json).unwrap();
1400 if let RelayerStatus::Evm { nonce, balance, .. } = status {
1401 assert_eq!(nonce, "42");
1402 assert_eq!(balance, "1000000000000000000");
1403 } else {
1404 panic!("Expected EVM status");
1405 }
1406
1407 let solana_json = r#"{
1409 "network_type": "solana",
1410 "balance": "5000000000",
1411 "pending_transactions_count": 3,
1412 "last_confirmed_transaction_timestamp": null,
1413 "system_disabled": false,
1414 "paused": true
1415 }"#;
1416
1417 let status: RelayerStatus = serde_json::from_str(solana_json).unwrap();
1418 if let RelayerStatus::Solana {
1419 balance, paused, ..
1420 } = status
1421 {
1422 assert_eq!(balance, "5000000000");
1423 assert!(paused);
1424 } else {
1425 panic!("Expected Solana status");
1426 }
1427
1428 let stellar_json = r#"{
1430 "network_type": "stellar",
1431 "balance": "1000000000",
1432 "pending_transactions_count": 2,
1433 "last_confirmed_transaction_timestamp": "2024-01-01T12:00:00Z",
1434 "system_disabled": true,
1435 "paused": false,
1436 "sequence_number": "123456789"
1437 }"#;
1438
1439 let status: RelayerStatus = serde_json::from_str(stellar_json).unwrap();
1440 if let RelayerStatus::Stellar {
1441 sequence_number,
1442 system_disabled,
1443 ..
1444 } = status
1445 {
1446 assert_eq!(sequence_number, "123456789");
1447 assert!(system_disabled);
1448 } else {
1449 panic!("Expected Stellar status");
1450 }
1451 }
1452}