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_MIN_BALANCE, DEFAULT_SOLANA_MAX_TX_DATA_SIZE,
22 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 }
341 RelayerNetworkPolicy::Solana(solana_policy) => {
342 solana_policy.allowed_programs.is_none()
343 && solana_policy.max_signatures.is_none()
344 && solana_policy.max_tx_data_size.is_none()
345 && solana_policy.min_balance.is_none()
346 && solana_policy.allowed_tokens.is_none()
347 && solana_policy.fee_payment_strategy.is_none()
348 && solana_policy.fee_margin_percentage.is_none()
349 && solana_policy.allowed_accounts.is_none()
350 && solana_policy.disallowed_accounts.is_none()
351 && solana_policy.max_allowed_fee_lamports.is_none()
352 && solana_policy.swap_config.is_none()
353 }
354 RelayerNetworkPolicy::Stellar(stellar_policy) => {
355 stellar_policy.min_balance.is_none()
356 && stellar_policy.max_fee.is_none()
357 && stellar_policy.timeout_seconds.is_none()
358 && stellar_policy.concurrent_transactions.is_none()
359 && stellar_policy.allowed_tokens.is_none()
360 && stellar_policy.fee_payment_strategy.is_none()
361 && stellar_policy.slippage_percentage.is_none()
362 && stellar_policy.fee_margin_percentage.is_none()
363 && stellar_policy.swap_config.is_none()
364 }
365 }
366}
367
368#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, ToSchema)]
370pub struct NetworkPolicyResponse {
371 #[serde(flatten)]
372 pub policy: RelayerNetworkPolicy,
373}
374
375fn default_evm_min_balance() -> u128 {
377 DEFAULT_EVM_MIN_BALANCE
378}
379
380fn default_evm_gas_limit_estimation() -> bool {
381 DEFAULT_EVM_GAS_LIMIT_ESTIMATION
382}
383
384fn default_solana_min_balance() -> u64 {
386 DEFAULT_SOLANA_MIN_BALANCE
387}
388
389fn default_stellar_min_balance() -> u64 {
391 DEFAULT_STELLAR_MIN_BALANCE
392}
393
394fn default_solana_max_tx_data_size() -> u16 {
396 DEFAULT_SOLANA_MAX_TX_DATA_SIZE
397}
398#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, ToSchema)]
400#[serde(deny_unknown_fields)]
401pub struct EvmPolicyResponse {
402 #[serde(
403 default = "default_evm_min_balance",
404 serialize_with = "crate::utils::serialize_u128_as_number",
405 deserialize_with = "crate::utils::deserialize_u128_as_number"
406 )]
407 #[schema(nullable = false)]
408 pub min_balance: u128,
409 #[serde(default = "default_evm_gas_limit_estimation")]
410 #[schema(nullable = false)]
411 pub gas_limit_estimation: bool,
412 #[serde(
413 skip_serializing_if = "Option::is_none",
414 serialize_with = "crate::utils::serialize_optional_u128_as_number",
415 deserialize_with = "crate::utils::deserialize_optional_u128_as_number",
416 default
417 )]
418 #[schema(nullable = false)]
419 pub gas_price_cap: Option<u128>,
420 #[serde(skip_serializing_if = "Option::is_none")]
421 #[schema(nullable = false)]
422 pub whitelist_receivers: Option<Vec<String>>,
423 #[serde(skip_serializing_if = "Option::is_none")]
424 #[schema(nullable = false)]
425 pub eip1559_pricing: Option<bool>,
426 #[serde(skip_serializing_if = "Option::is_none")]
427 #[schema(nullable = false)]
428 pub private_transactions: Option<bool>,
429}
430
431#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, ToSchema)]
433#[serde(deny_unknown_fields)]
434pub struct SolanaPolicyResponse {
435 #[serde(skip_serializing_if = "Option::is_none")]
436 #[schema(nullable = false)]
437 pub allowed_programs: Option<Vec<String>>,
438 #[serde(skip_serializing_if = "Option::is_none")]
439 #[schema(nullable = false)]
440 pub max_signatures: Option<u8>,
441 #[schema(nullable = false)]
442 #[serde(default = "default_solana_max_tx_data_size")]
443 pub max_tx_data_size: u16,
444 #[serde(default = "default_solana_min_balance")]
445 #[schema(nullable = false)]
446 pub min_balance: u64,
447 #[serde(skip_serializing_if = "Option::is_none")]
448 #[schema(nullable = false)]
449 pub allowed_tokens: Option<Vec<SolanaAllowedTokensPolicy>>,
450 #[serde(skip_serializing_if = "Option::is_none")]
451 #[schema(nullable = false)]
452 pub fee_payment_strategy: Option<SolanaFeePaymentStrategy>,
453 #[serde(skip_serializing_if = "Option::is_none")]
454 #[schema(nullable = false)]
455 pub fee_margin_percentage: Option<f32>,
456 #[serde(skip_serializing_if = "Option::is_none")]
457 #[schema(nullable = false)]
458 pub allowed_accounts: Option<Vec<String>>,
459 #[serde(skip_serializing_if = "Option::is_none")]
460 #[schema(nullable = false)]
461 pub disallowed_accounts: Option<Vec<String>>,
462 #[serde(skip_serializing_if = "Option::is_none")]
463 #[schema(nullable = false)]
464 pub max_allowed_fee_lamports: Option<u64>,
465 #[serde(skip_serializing_if = "Option::is_none")]
466 #[schema(nullable = false)]
467 pub swap_config: Option<RelayerSolanaSwapConfig>,
468}
469
470#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, ToSchema)]
472#[serde(deny_unknown_fields)]
473pub struct StellarPolicyResponse {
474 #[serde(skip_serializing_if = "Option::is_none")]
475 #[schema(nullable = false)]
476 pub max_fee: Option<u32>,
477 #[serde(skip_serializing_if = "Option::is_none")]
478 #[schema(nullable = false)]
479 pub timeout_seconds: Option<u64>,
480 #[serde(default = "default_stellar_min_balance")]
481 #[schema(nullable = false)]
482 pub min_balance: u64,
483 #[serde(skip_serializing_if = "Option::is_none")]
484 #[schema(nullable = false)]
485 pub concurrent_transactions: Option<bool>,
486 #[serde(skip_serializing_if = "Option::is_none")]
487 #[schema(nullable = false)]
488 pub allowed_tokens: Option<Vec<StellarAllowedTokensPolicy>>,
489 #[serde(skip_serializing_if = "Option::is_none")]
490 #[schema(nullable = false)]
491 pub fee_payment_strategy: Option<StellarFeePaymentStrategy>,
492 #[serde(skip_serializing_if = "Option::is_none")]
493 #[schema(nullable = false)]
494 pub slippage_percentage: Option<f32>,
495 #[serde(skip_serializing_if = "Option::is_none")]
496 #[schema(nullable = false)]
497 pub fee_margin_percentage: Option<f32>,
498 #[serde(skip_serializing_if = "Option::is_none")]
499 #[schema(nullable = false)]
500 pub swap_config: Option<RelayerStellarSwapConfig>,
501}
502
503impl From<RelayerEvmPolicy> for EvmPolicyResponse {
504 fn from(policy: RelayerEvmPolicy) -> Self {
505 Self {
506 min_balance: policy.min_balance.unwrap_or(DEFAULT_EVM_MIN_BALANCE),
507 gas_limit_estimation: policy
508 .gas_limit_estimation
509 .unwrap_or(DEFAULT_EVM_GAS_LIMIT_ESTIMATION),
510 gas_price_cap: policy.gas_price_cap,
511 whitelist_receivers: policy.whitelist_receivers,
512 eip1559_pricing: policy.eip1559_pricing,
513 private_transactions: policy.private_transactions,
514 }
515 }
516}
517
518impl From<RelayerSolanaPolicy> for SolanaPolicyResponse {
519 fn from(policy: RelayerSolanaPolicy) -> Self {
520 Self {
521 allowed_programs: policy.allowed_programs,
522 max_signatures: policy.max_signatures,
523 max_tx_data_size: policy
524 .max_tx_data_size
525 .unwrap_or(DEFAULT_SOLANA_MAX_TX_DATA_SIZE),
526 min_balance: policy.min_balance.unwrap_or(DEFAULT_SOLANA_MIN_BALANCE),
527 allowed_tokens: policy.allowed_tokens,
528 fee_payment_strategy: policy.fee_payment_strategy,
529 fee_margin_percentage: policy.fee_margin_percentage,
530 allowed_accounts: policy.allowed_accounts,
531 disallowed_accounts: policy.disallowed_accounts,
532 max_allowed_fee_lamports: policy.max_allowed_fee_lamports,
533 swap_config: policy.swap_config,
534 }
535 }
536}
537
538impl From<RelayerStellarPolicy> for StellarPolicyResponse {
539 fn from(policy: RelayerStellarPolicy) -> Self {
540 Self {
541 min_balance: policy.min_balance.unwrap_or(DEFAULT_STELLAR_MIN_BALANCE),
542 max_fee: policy.max_fee,
543 timeout_seconds: policy.timeout_seconds,
544 concurrent_transactions: policy.concurrent_transactions,
545 allowed_tokens: policy.allowed_tokens,
546 fee_payment_strategy: policy.fee_payment_strategy,
547 slippage_percentage: policy.slippage_percentage,
548 fee_margin_percentage: policy.fee_margin_percentage,
549 swap_config: policy.swap_config,
550 }
551 }
552}
553
554#[cfg(test)]
555mod tests {
556 use super::*;
557 use crate::models::{
558 relayer::{
559 RelayerEvmPolicy, RelayerSolanaPolicy, RelayerSolanaSwapConfig, RelayerStellarPolicy,
560 SolanaAllowedTokensPolicy, SolanaFeePaymentStrategy, SolanaSwapStrategy,
561 StellarAllowedTokensPolicy, StellarFeePaymentStrategy, StellarSwapStrategy,
562 },
563 StellarTokenKind, StellarTokenMetadata,
564 };
565
566 #[test]
567 fn test_from_domain_relayer() {
568 let relayer = Relayer::new(
569 "test-relayer".to_string(),
570 "Test Relayer".to_string(),
571 "mainnet".to_string(),
572 false,
573 RelayerNetworkType::Evm,
574 Some(RelayerNetworkPolicy::Evm(RelayerEvmPolicy {
575 gas_price_cap: Some(100_000_000_000),
576 whitelist_receivers: None,
577 eip1559_pricing: Some(true),
578 private_transactions: None,
579 min_balance: None,
580 gas_limit_estimation: None,
581 })),
582 "test-signer".to_string(),
583 None,
584 None,
585 );
586
587 let response: RelayerResponse = relayer.clone().into();
588
589 assert_eq!(response.id, relayer.id);
590 assert_eq!(response.name, relayer.name);
591 assert_eq!(response.network, relayer.network);
592 assert_eq!(response.network_type, relayer.network_type);
593 assert_eq!(response.paused, relayer.paused);
594 assert_eq!(
595 response.policies,
596 Some(RelayerNetworkPolicyResponse::Evm(
597 RelayerEvmPolicy {
598 gas_price_cap: Some(100_000_000_000),
599 whitelist_receivers: None,
600 eip1559_pricing: Some(true),
601 private_transactions: None,
602 min_balance: Some(DEFAULT_EVM_MIN_BALANCE),
603 gas_limit_estimation: Some(DEFAULT_EVM_GAS_LIMIT_ESTIMATION),
604 }
605 .into()
606 ))
607 );
608 assert_eq!(response.signer_id, relayer.signer_id);
609 assert_eq!(response.notification_id, relayer.notification_id);
610 assert_eq!(response.custom_rpc_urls, None);
612 assert_eq!(response.address, None);
613 assert_eq!(response.system_disabled, None);
614 }
615
616 #[test]
617 fn test_from_domain_relayer_solana() {
618 let relayer = Relayer::new(
619 "test-solana-relayer".to_string(),
620 "Test Solana Relayer".to_string(),
621 "mainnet".to_string(),
622 false,
623 RelayerNetworkType::Solana,
624 Some(RelayerNetworkPolicy::Solana(RelayerSolanaPolicy {
625 allowed_programs: Some(vec!["11111111111111111111111111111111".to_string()]),
626 max_signatures: Some(5),
627 min_balance: Some(1000000),
628 fee_payment_strategy: Some(SolanaFeePaymentStrategy::Relayer),
629 allowed_tokens: Some(vec![SolanaAllowedTokensPolicy::new(
630 "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v".to_string(),
631 Some(100000),
632 None,
633 )]),
634 max_tx_data_size: None,
635 fee_margin_percentage: None,
636 allowed_accounts: None,
637 disallowed_accounts: None,
638 max_allowed_fee_lamports: None,
639 swap_config: None,
640 })),
641 "test-signer".to_string(),
642 None,
643 None,
644 );
645
646 let response: RelayerResponse = relayer.clone().into();
647
648 assert_eq!(response.id, relayer.id);
649 assert_eq!(response.network_type, RelayerNetworkType::Solana);
650 assert!(response.policies.is_some());
651
652 if let Some(RelayerNetworkPolicyResponse::Solana(solana_response)) = response.policies {
653 assert_eq!(solana_response.min_balance, 1000000);
654 assert_eq!(solana_response.max_signatures, Some(5));
655 } else {
656 panic!("Expected Solana policy response");
657 }
658 }
659
660 #[test]
661 fn test_from_domain_relayer_stellar() {
662 let relayer = Relayer::new(
663 "test-stellar-relayer".to_string(),
664 "Test Stellar Relayer".to_string(),
665 "mainnet".to_string(),
666 false,
667 RelayerNetworkType::Stellar,
668 Some(RelayerNetworkPolicy::Stellar(RelayerStellarPolicy {
669 min_balance: Some(20000000),
670 max_fee: Some(100000),
671 timeout_seconds: Some(30),
672 concurrent_transactions: None,
673 allowed_tokens: None,
674 fee_payment_strategy: Some(StellarFeePaymentStrategy::Relayer),
675 slippage_percentage: None,
676 fee_margin_percentage: None,
677 swap_config: None,
678 })),
679 "test-signer".to_string(),
680 None,
681 None,
682 );
683
684 let response: RelayerResponse = relayer.clone().into();
685
686 assert_eq!(response.id, relayer.id);
687 assert_eq!(response.network_type, RelayerNetworkType::Stellar);
688 assert!(response.policies.is_some());
689
690 if let Some(RelayerNetworkPolicyResponse::Stellar(stellar_response)) = response.policies {
691 assert_eq!(stellar_response.min_balance, 20000000);
692 } else {
693 panic!("Expected Stellar policy response");
694 }
695 }
696
697 #[test]
698 fn test_response_serialization() {
699 let response = RelayerResponse {
700 id: "test-relayer".to_string(),
701 name: "Test Relayer".to_string(),
702 network: "mainnet".to_string(),
703 network_type: RelayerNetworkType::Evm,
704 paused: false,
705 policies: Some(RelayerNetworkPolicyResponse::Evm(EvmPolicyResponse {
706 gas_price_cap: Some(50000000000),
707 whitelist_receivers: None,
708 eip1559_pricing: Some(true),
709 private_transactions: None,
710 min_balance: DEFAULT_EVM_MIN_BALANCE,
711 gas_limit_estimation: DEFAULT_EVM_GAS_LIMIT_ESTIMATION,
712 })),
713 signer_id: "test-signer".to_string(),
714 notification_id: None,
715 custom_rpc_urls: None,
716 address: Some("0x123...".to_string()),
717 system_disabled: Some(false),
718 ..Default::default()
719 };
720
721 let serialized = serde_json::to_string(&response).unwrap();
723 assert!(!serialized.is_empty());
724
725 let deserialized: RelayerResponse = serde_json::from_str(&serialized).unwrap();
727 assert_eq!(response.id, deserialized.id);
728 assert_eq!(response.name, deserialized.name);
729 }
730
731 #[test]
732 fn test_solana_response_serialization() {
733 let response = RelayerResponse {
734 id: "test-solana-relayer".to_string(),
735 name: "Test Solana Relayer".to_string(),
736 network: "mainnet".to_string(),
737 network_type: RelayerNetworkType::Solana,
738 paused: false,
739 policies: Some(RelayerNetworkPolicyResponse::Solana(SolanaPolicyResponse {
740 allowed_programs: Some(vec!["11111111111111111111111111111111".to_string()]),
741 max_signatures: Some(5),
742 max_tx_data_size: DEFAULT_SOLANA_MAX_TX_DATA_SIZE,
743 min_balance: 1000000,
744 allowed_tokens: Some(vec![SolanaAllowedTokensPolicy::new(
745 "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v".to_string(),
746 Some(100000),
747 None,
748 )]),
749 fee_payment_strategy: Some(SolanaFeePaymentStrategy::Relayer),
750 fee_margin_percentage: Some(5.0),
751 allowed_accounts: None,
752 disallowed_accounts: None,
753 max_allowed_fee_lamports: Some(500000),
754 swap_config: Some(RelayerSolanaSwapConfig {
755 strategy: Some(SolanaSwapStrategy::JupiterSwap),
756 cron_schedule: Some("0 0 * * *".to_string()),
757 min_balance_threshold: Some(500000),
758 jupiter_swap_options: None,
759 }),
760 })),
761 signer_id: "test-signer".to_string(),
762 notification_id: None,
763 custom_rpc_urls: None,
764 address: Some("SolanaAddress123...".to_string()),
765 system_disabled: Some(false),
766 ..Default::default()
767 };
768
769 let serialized = serde_json::to_string(&response).unwrap();
771 assert!(!serialized.is_empty());
772
773 let deserialized: RelayerResponse = serde_json::from_str(&serialized).unwrap();
775 assert_eq!(response.id, deserialized.id);
776 assert_eq!(response.network_type, RelayerNetworkType::Solana);
777 }
778
779 #[test]
780 fn test_stellar_response_serialization() {
781 let response = RelayerResponse {
782 id: "test-stellar-relayer".to_string(),
783 name: "Test Stellar Relayer".to_string(),
784 network: "mainnet".to_string(),
785 network_type: RelayerNetworkType::Stellar,
786 paused: false,
787 policies: Some(RelayerNetworkPolicyResponse::Stellar(
788 StellarPolicyResponse {
789 max_fee: Some(5000),
790 timeout_seconds: None,
791 min_balance: 20000000,
792 concurrent_transactions: None,
793 allowed_tokens: None,
794 fee_payment_strategy: None,
795 slippage_percentage: None,
796 fee_margin_percentage: None,
797 swap_config: None,
798 },
799 )),
800 signer_id: "test-signer".to_string(),
801 notification_id: None,
802 custom_rpc_urls: None,
803 address: Some("GXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX".to_string()),
804 system_disabled: Some(false),
805 ..Default::default()
806 };
807
808 let serialized = serde_json::to_string(&response).unwrap();
810 assert!(!serialized.is_empty());
811
812 let deserialized: RelayerResponse = serde_json::from_str(&serialized).unwrap();
814 assert_eq!(response.id, deserialized.id);
815 assert_eq!(response.network_type, RelayerNetworkType::Stellar);
816
817 if let Some(RelayerNetworkPolicyResponse::Stellar(stellar_policy)) = deserialized.policies {
819 assert_eq!(stellar_policy.min_balance, 20000000);
820 assert_eq!(stellar_policy.max_fee, Some(5000));
821 assert_eq!(stellar_policy.timeout_seconds, None);
822 } else {
823 panic!("Expected Stellar policy in deserialized response");
824 }
825 }
826
827 #[test]
828 fn test_response_without_redundant_network_type() {
829 let response = RelayerResponse {
830 id: "test-relayer".to_string(),
831 name: "Test Relayer".to_string(),
832 network: "mainnet".to_string(),
833 network_type: RelayerNetworkType::Evm,
834 paused: false,
835 policies: Some(RelayerNetworkPolicyResponse::Evm(EvmPolicyResponse {
836 gas_price_cap: Some(100_000_000_000),
837 whitelist_receivers: None,
838 eip1559_pricing: Some(true),
839 private_transactions: None,
840 min_balance: DEFAULT_EVM_MIN_BALANCE,
841 gas_limit_estimation: DEFAULT_EVM_GAS_LIMIT_ESTIMATION,
842 })),
843 signer_id: "test-signer".to_string(),
844 notification_id: None,
845 custom_rpc_urls: None,
846 address: Some("0x123...".to_string()),
847 system_disabled: Some(false),
848 ..Default::default()
849 };
850
851 let serialized = serde_json::to_string_pretty(&response).unwrap();
852
853 assert!(serialized.contains(r#""network_type": "evm""#));
854
855 let network_type_count = serialized.matches(r#""network_type""#).count();
857 assert_eq!(
858 network_type_count, 1,
859 "Should only have one network_type field at top level, not in policies"
860 );
861
862 assert!(serialized.contains(r#""gas_price_cap": 100000000000"#));
863 assert!(serialized.contains(r#""eip1559_pricing": true"#));
864 }
865
866 #[test]
867 fn test_solana_response_without_redundant_network_type() {
868 let response = RelayerResponse {
869 id: "test-solana-relayer".to_string(),
870 name: "Test Solana Relayer".to_string(),
871 network: "mainnet".to_string(),
872 network_type: RelayerNetworkType::Solana,
873 paused: false,
874 policies: Some(RelayerNetworkPolicyResponse::Solana(SolanaPolicyResponse {
875 allowed_programs: Some(vec!["11111111111111111111111111111111".to_string()]),
876 max_signatures: Some(5),
877 max_tx_data_size: DEFAULT_SOLANA_MAX_TX_DATA_SIZE,
878 min_balance: 1000000,
879 allowed_tokens: None,
880 fee_payment_strategy: Some(SolanaFeePaymentStrategy::Relayer),
881 fee_margin_percentage: None,
882 allowed_accounts: None,
883 disallowed_accounts: None,
884 max_allowed_fee_lamports: None,
885 swap_config: None,
886 })),
887 signer_id: "test-signer".to_string(),
888 notification_id: None,
889 custom_rpc_urls: None,
890 address: Some("SolanaAddress123...".to_string()),
891 system_disabled: Some(false),
892 ..Default::default()
893 };
894
895 let serialized = serde_json::to_string_pretty(&response).unwrap();
896
897 assert!(serialized.contains(r#""network_type": "solana""#));
898
899 let network_type_count = serialized.matches(r#""network_type""#).count();
901 assert_eq!(
902 network_type_count, 1,
903 "Should only have one network_type field at top level, not in policies"
904 );
905
906 assert!(serialized.contains(r#""max_signatures": 5"#));
907 assert!(serialized.contains(r#""fee_payment_strategy": "relayer""#));
908 }
909
910 #[test]
911 fn test_stellar_response_without_redundant_network_type() {
912 let response = RelayerResponse {
913 id: "test-stellar-relayer".to_string(),
914 name: "Test Stellar Relayer".to_string(),
915 network: "mainnet".to_string(),
916 network_type: RelayerNetworkType::Stellar,
917 paused: false,
918 policies: Some(RelayerNetworkPolicyResponse::Stellar(
919 StellarPolicyResponse {
920 min_balance: 20000000,
921 max_fee: Some(100000),
922 timeout_seconds: Some(30),
923 concurrent_transactions: None,
924 allowed_tokens: None,
925 fee_payment_strategy: None,
926 slippage_percentage: None,
927 fee_margin_percentage: None,
928 swap_config: None,
929 },
930 )),
931 signer_id: "test-signer".to_string(),
932 notification_id: None,
933 custom_rpc_urls: None,
934 address: Some("GXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX".to_string()),
935 system_disabled: Some(false),
936 ..Default::default()
937 };
938
939 let serialized = serde_json::to_string_pretty(&response).unwrap();
940
941 assert!(serialized.contains(r#""network_type": "stellar""#));
942
943 let network_type_count = serialized.matches(r#""network_type""#).count();
945 assert_eq!(
946 network_type_count, 1,
947 "Should only have one network_type field at top level, not in policies"
948 );
949
950 assert!(serialized.contains(r#""min_balance": 20000000"#));
951 assert!(serialized.contains(r#""max_fee": 100000"#));
952 assert!(serialized.contains(r#""timeout_seconds": 30"#));
953 }
954
955 #[test]
956 fn test_empty_policies_not_returned_in_response() {
957 let repo_model = RelayerRepoModel {
959 id: "test-relayer".to_string(),
960 name: "Test Relayer".to_string(),
961 network: "mainnet".to_string(),
962 network_type: RelayerNetworkType::Evm,
963 paused: false,
964 policies: RelayerNetworkPolicy::Evm(RelayerEvmPolicy::default()), signer_id: "test-signer".to_string(),
966 notification_id: None,
967 custom_rpc_urls: None,
968 address: "0x123...".to_string(),
969 system_disabled: false,
970 ..Default::default()
971 };
972
973 let response = RelayerResponse::from(repo_model);
975
976 assert_eq!(response.policies, None);
978
979 let serialized = serde_json::to_string(&response).unwrap();
981 assert!(
982 !serialized.contains("policies"),
983 "Empty policies should not appear in JSON response"
984 );
985 }
986
987 #[test]
988 fn test_empty_solana_policies_not_returned_in_response() {
989 let repo_model = RelayerRepoModel {
991 id: "test-solana-relayer".to_string(),
992 name: "Test Solana Relayer".to_string(),
993 network: "mainnet".to_string(),
994 network_type: RelayerNetworkType::Solana,
995 paused: false,
996 policies: RelayerNetworkPolicy::Solana(RelayerSolanaPolicy::default()), signer_id: "test-signer".to_string(),
998 notification_id: None,
999 custom_rpc_urls: None,
1000 address: "SolanaAddress123...".to_string(),
1001 system_disabled: false,
1002 ..Default::default()
1003 };
1004
1005 let response = RelayerResponse::from(repo_model);
1007
1008 assert_eq!(response.policies, None);
1010
1011 let serialized = serde_json::to_string(&response).unwrap();
1013 assert!(
1014 !serialized.contains("policies"),
1015 "Empty Solana policies should not appear in JSON response"
1016 );
1017 }
1018
1019 #[test]
1020 fn test_empty_stellar_policies_not_returned_in_response() {
1021 let repo_model = RelayerRepoModel {
1023 id: "test-stellar-relayer".to_string(),
1024 name: "Test Stellar Relayer".to_string(),
1025 network: "mainnet".to_string(),
1026 network_type: RelayerNetworkType::Stellar,
1027 paused: false,
1028 policies: RelayerNetworkPolicy::Stellar(RelayerStellarPolicy::default()), signer_id: "test-signer".to_string(),
1030 notification_id: None,
1031 custom_rpc_urls: None,
1032 address: "GXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX".to_string(),
1033 system_disabled: false,
1034 ..Default::default()
1035 };
1036
1037 let response = RelayerResponse::from(repo_model);
1039
1040 assert_eq!(response.policies, None);
1042
1043 let serialized = serde_json::to_string(&response).unwrap();
1045 assert!(
1046 !serialized.contains("policies"),
1047 "Empty Stellar policies should not appear in JSON response"
1048 );
1049 }
1050
1051 #[test]
1052 fn test_user_provided_policies_returned_in_response() {
1053 let repo_model = RelayerRepoModel {
1055 id: "test-relayer".to_string(),
1056 name: "Test Relayer".to_string(),
1057 network: "mainnet".to_string(),
1058 network_type: RelayerNetworkType::Evm,
1059 paused: false,
1060 policies: RelayerNetworkPolicy::Evm(RelayerEvmPolicy {
1061 gas_price_cap: Some(100_000_000_000),
1062 eip1559_pricing: Some(true),
1063 min_balance: None, gas_limit_estimation: None,
1065 whitelist_receivers: None,
1066 private_transactions: None,
1067 }),
1068 signer_id: "test-signer".to_string(),
1069 notification_id: None,
1070 custom_rpc_urls: None,
1071 address: "0x123...".to_string(),
1072 system_disabled: false,
1073 ..Default::default()
1074 };
1075
1076 let response = RelayerResponse::from(repo_model);
1078
1079 assert!(response.policies.is_some());
1081
1082 let serialized = serde_json::to_string(&response).unwrap();
1084 assert!(
1085 serialized.contains("policies"),
1086 "User-provided policies should appear in JSON response"
1087 );
1088 assert!(
1089 serialized.contains("gas_price_cap"),
1090 "User-provided policy values should appear in JSON response"
1091 );
1092 }
1093
1094 #[test]
1095 fn test_user_provided_solana_policies_returned_in_response() {
1096 let repo_model = RelayerRepoModel {
1098 id: "test-solana-relayer".to_string(),
1099 name: "Test Solana Relayer".to_string(),
1100 network: "mainnet".to_string(),
1101 network_type: RelayerNetworkType::Solana,
1102 paused: false,
1103 policies: RelayerNetworkPolicy::Solana(RelayerSolanaPolicy {
1104 max_signatures: Some(5),
1105 fee_payment_strategy: Some(SolanaFeePaymentStrategy::Relayer),
1106 min_balance: Some(1000000),
1107 allowed_programs: None, max_tx_data_size: None,
1109 allowed_tokens: None,
1110 fee_margin_percentage: None,
1111 allowed_accounts: None,
1112 disallowed_accounts: None,
1113 max_allowed_fee_lamports: None,
1114 swap_config: None,
1115 }),
1116 signer_id: "test-signer".to_string(),
1117 notification_id: None,
1118 custom_rpc_urls: None,
1119 address: "SolanaAddress123...".to_string(),
1120 system_disabled: false,
1121 ..Default::default()
1122 };
1123
1124 let response = RelayerResponse::from(repo_model);
1126
1127 assert!(response.policies.is_some());
1129
1130 let serialized = serde_json::to_string(&response).unwrap();
1132 assert!(
1133 serialized.contains("policies"),
1134 "User-provided Solana policies should appear in JSON response"
1135 );
1136 assert!(
1137 serialized.contains("max_signatures"),
1138 "User-provided Solana policy values should appear in JSON response"
1139 );
1140 assert!(
1141 serialized.contains("fee_payment_strategy"),
1142 "User-provided Solana policy values should appear in JSON response"
1143 );
1144 }
1145
1146 #[test]
1147 fn test_user_provided_stellar_policies_returned_in_response() {
1148 let repo_model = RelayerRepoModel {
1150 id: "test-stellar-relayer".to_string(),
1151 name: "Test Stellar Relayer".to_string(),
1152 network: "mainnet".to_string(),
1153 network_type: RelayerNetworkType::Stellar,
1154 paused: false,
1155 policies: RelayerNetworkPolicy::Stellar(RelayerStellarPolicy {
1156 max_fee: Some(100000),
1157 timeout_seconds: Some(30),
1158 min_balance: Some(20000000),
1159 concurrent_transactions: Some(true),
1160 allowed_tokens: Some(vec![StellarAllowedTokensPolicy::new(
1161 "USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN".to_string(),
1162 Some(StellarTokenMetadata {
1163 kind: StellarTokenKind::Classic {
1164 code: "USDC".to_string(),
1165 issuer: "GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN"
1166 .to_string(),
1167 },
1168 decimals: 6,
1169 canonical_asset_id:
1170 "USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN"
1171 .to_string(),
1172 }),
1173 None,
1174 None,
1175 )]),
1176 fee_payment_strategy: Some(StellarFeePaymentStrategy::Relayer),
1177 slippage_percentage: Some(0.5),
1178 fee_margin_percentage: Some(2.0),
1179 swap_config: Some(RelayerStellarSwapConfig {
1180 strategies: vec![StellarSwapStrategy::Soroswap],
1181 cron_schedule: Some("0 0 * * *".to_string()),
1182 min_balance_threshold: Some(10000000),
1183 }),
1184 }),
1185 signer_id: "test-signer".to_string(),
1186 notification_id: None,
1187 custom_rpc_urls: None,
1188 address: "GXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX".to_string(),
1189 system_disabled: false,
1190 ..Default::default()
1191 };
1192
1193 let response = RelayerResponse::from(repo_model);
1195
1196 assert!(response.policies.is_some());
1198
1199 let serialized = serde_json::to_string(&response).unwrap();
1201 assert!(
1202 serialized.contains("policies"),
1203 "User-provided Stellar policies should appear in JSON response"
1204 );
1205 assert!(
1206 serialized.contains("max_fee"),
1207 "User-provided Stellar policy values should appear in JSON response"
1208 );
1209 assert!(
1210 serialized.contains("timeout_seconds"),
1211 "User-provided Stellar policy values should appear in JSON response"
1212 );
1213 assert!(
1214 serialized.contains("allowed_tokens"),
1215 "User-provided Stellar policy values should appear in JSON response"
1216 );
1217 assert!(
1218 serialized.contains("fee_payment_strategy"),
1219 "User-provided Stellar policy values should appear in JSON response"
1220 );
1221 assert!(
1222 serialized.contains("slippage_percentage"),
1223 "User-provided Stellar policy values should appear in JSON response"
1224 );
1225 assert!(
1226 serialized.contains("fee_margin_percentage"),
1227 "User-provided Stellar policy values should appear in JSON response"
1228 );
1229 assert!(
1230 serialized.contains("swap_config"),
1231 "User-provided Stellar policy values should appear in JSON response"
1232 );
1233 }
1234
1235 #[test]
1236 fn test_stellar_fee_payment_strategy_explicitly_set_vs_omitted() {
1237 let policy_with_user = RelayerStellarPolicy {
1239 min_balance: Some(20000000),
1240 max_fee: Some(100000),
1241 timeout_seconds: Some(30),
1242 concurrent_transactions: None,
1243 allowed_tokens: None,
1244 fee_payment_strategy: Some(StellarFeePaymentStrategy::User),
1245 slippage_percentage: None,
1246 fee_margin_percentage: None,
1247 swap_config: None,
1248 };
1249
1250 let response_with_user = StellarPolicyResponse::from(policy_with_user);
1251 let serialized_with_user = serde_json::to_string(&response_with_user).unwrap();
1252 assert!(
1253 serialized_with_user.contains(r#""fee_payment_strategy":"user""#),
1254 "Explicitly set User fee_payment_strategy should appear in JSON response"
1255 );
1256
1257 let policy_with_relayer = RelayerStellarPolicy {
1259 min_balance: Some(20000000),
1260 max_fee: Some(100000),
1261 timeout_seconds: Some(30),
1262 concurrent_transactions: None,
1263 allowed_tokens: None,
1264 fee_payment_strategy: Some(StellarFeePaymentStrategy::Relayer),
1265 slippage_percentage: None,
1266 fee_margin_percentage: None,
1267 swap_config: None,
1268 };
1269
1270 let response_with_relayer = StellarPolicyResponse::from(policy_with_relayer);
1271 let serialized_with_relayer = serde_json::to_string(&response_with_relayer).unwrap();
1272 assert!(
1273 serialized_with_relayer.contains(r#""fee_payment_strategy":"relayer""#),
1274 "Explicitly set Relayer fee_payment_strategy should appear in JSON response"
1275 );
1276
1277 let policy_omitted = RelayerStellarPolicy {
1279 min_balance: Some(20000000),
1280 max_fee: Some(100000),
1281 timeout_seconds: Some(30),
1282 concurrent_transactions: None,
1283 allowed_tokens: None,
1284 fee_payment_strategy: None,
1285 slippage_percentage: None,
1286 fee_margin_percentage: None,
1287 swap_config: None,
1288 };
1289
1290 let response_omitted = StellarPolicyResponse::from(policy_omitted);
1291 let serialized_omitted = serde_json::to_string(&response_omitted).unwrap();
1292 assert!(
1293 !serialized_omitted.contains("fee_payment_strategy"),
1294 "Omitted fee_payment_strategy (None) should NOT appear in JSON response"
1295 );
1296
1297 let empty_policy = RelayerStellarPolicy::default();
1299 assert!(
1300 is_empty_policy(&RelayerNetworkPolicy::Stellar(empty_policy)),
1301 "Policy with all None values should be considered empty"
1302 );
1303
1304 let policy_with_user_only = RelayerStellarPolicy {
1305 fee_payment_strategy: Some(StellarFeePaymentStrategy::User),
1306 ..Default::default()
1307 };
1308 assert!(
1309 !is_empty_policy(&RelayerNetworkPolicy::Stellar(policy_with_user_only)),
1310 "Policy with explicitly set User fee_payment_strategy should NOT be considered empty"
1311 );
1312
1313 let policy_with_relayer_only = RelayerStellarPolicy {
1314 fee_payment_strategy: Some(StellarFeePaymentStrategy::Relayer),
1315 ..Default::default()
1316 };
1317 assert!(
1318 !is_empty_policy(&RelayerNetworkPolicy::Stellar(policy_with_relayer_only)),
1319 "Policy with explicitly set Relayer fee_payment_strategy should NOT be considered empty"
1320 );
1321 }
1322
1323 #[test]
1324 fn test_relayer_status_serialization() {
1325 let evm_status = RelayerStatus::Evm {
1327 balance: "1000000000000000000".to_string(),
1328 pending_transactions_count: 5,
1329 last_confirmed_transaction_timestamp: Some("2024-01-01T00:00:00Z".to_string()),
1330 system_disabled: false,
1331 paused: false,
1332 nonce: "42".to_string(),
1333 };
1334
1335 let serialized = serde_json::to_string(&evm_status).unwrap();
1336 assert!(serialized.contains(r#""network_type":"evm""#));
1337 assert!(serialized.contains(r#""nonce":"42""#));
1338 assert!(serialized.contains(r#""balance":"1000000000000000000""#));
1339
1340 let solana_status = RelayerStatus::Solana {
1342 balance: "5000000000".to_string(),
1343 pending_transactions_count: 3,
1344 last_confirmed_transaction_timestamp: None,
1345 system_disabled: false,
1346 paused: true,
1347 };
1348
1349 let serialized = serde_json::to_string(&solana_status).unwrap();
1350 assert!(serialized.contains(r#""network_type":"solana""#));
1351 assert!(serialized.contains(r#""balance":"5000000000""#));
1352 assert!(serialized.contains(r#""paused":true"#));
1353
1354 let stellar_status = RelayerStatus::Stellar {
1356 balance: "1000000000".to_string(),
1357 pending_transactions_count: 2,
1358 last_confirmed_transaction_timestamp: Some("2024-01-01T12:00:00Z".to_string()),
1359 system_disabled: true,
1360 paused: false,
1361 sequence_number: "123456789".to_string(),
1362 };
1363
1364 let serialized = serde_json::to_string(&stellar_status).unwrap();
1365 assert!(serialized.contains(r#""network_type":"stellar""#));
1366 assert!(serialized.contains(r#""sequence_number":"123456789""#));
1367 assert!(serialized.contains(r#""system_disabled":true"#));
1368 }
1369
1370 #[test]
1371 fn test_relayer_status_deserialization() {
1372 let evm_json = r#"{
1374 "network_type": "evm",
1375 "balance": "1000000000000000000",
1376 "pending_transactions_count": 5,
1377 "last_confirmed_transaction_timestamp": "2024-01-01T00:00:00Z",
1378 "system_disabled": false,
1379 "paused": false,
1380 "nonce": "42"
1381 }"#;
1382
1383 let status: RelayerStatus = serde_json::from_str(evm_json).unwrap();
1384 if let RelayerStatus::Evm { nonce, balance, .. } = status {
1385 assert_eq!(nonce, "42");
1386 assert_eq!(balance, "1000000000000000000");
1387 } else {
1388 panic!("Expected EVM status");
1389 }
1390
1391 let solana_json = r#"{
1393 "network_type": "solana",
1394 "balance": "5000000000",
1395 "pending_transactions_count": 3,
1396 "last_confirmed_transaction_timestamp": null,
1397 "system_disabled": false,
1398 "paused": true
1399 }"#;
1400
1401 let status: RelayerStatus = serde_json::from_str(solana_json).unwrap();
1402 if let RelayerStatus::Solana {
1403 balance, paused, ..
1404 } = status
1405 {
1406 assert_eq!(balance, "5000000000");
1407 assert!(paused);
1408 } else {
1409 panic!("Expected Solana status");
1410 }
1411
1412 let stellar_json = r#"{
1414 "network_type": "stellar",
1415 "balance": "1000000000",
1416 "pending_transactions_count": 2,
1417 "last_confirmed_transaction_timestamp": "2024-01-01T12:00:00Z",
1418 "system_disabled": true,
1419 "paused": false,
1420 "sequence_number": "123456789"
1421 }"#;
1422
1423 let status: RelayerStatus = serde_json::from_str(stellar_json).unwrap();
1424 if let RelayerStatus::Stellar {
1425 sequence_number,
1426 system_disabled,
1427 ..
1428 } = status
1429 {
1430 assert_eq!(sequence_number, "123456789");
1431 assert!(system_disabled);
1432 } else {
1433 panic!("Expected Stellar status");
1434 }
1435 }
1436}