1use super::{
15 Relayer, RelayerEvmPolicy, RelayerNetworkPolicy, RelayerNetworkType, RelayerSolanaPolicy,
16 RelayerStellarPolicy, RpcConfig,
17};
18use crate::{models::error::ApiError, utils::generate_uuid};
19use serde::{Deserialize, Serialize};
20use utoipa::ToSchema;
21
22#[derive(Debug, Clone, Serialize, ToSchema)]
24#[serde(deny_unknown_fields)]
25pub struct CreateRelayerRequest {
26 #[schema(nullable = false)]
27 pub id: Option<String>,
28 pub name: String,
29 pub network: String,
30 pub paused: bool,
31 pub network_type: RelayerNetworkType,
32 #[serde(skip_serializing_if = "Option::is_none")]
34 #[schema(nullable = false)]
35 pub policies: Option<CreateRelayerPolicyRequest>,
36 #[schema(nullable = false)]
37 pub signer_id: String,
38 #[schema(nullable = false)]
39 pub notification_id: Option<String>,
40 #[schema(nullable = false)]
41 pub custom_rpc_urls: Option<Vec<RpcConfig>>,
42}
43
44#[derive(Debug, Clone, Deserialize)]
46#[serde(deny_unknown_fields)]
47struct CreateRelayerRequestRaw {
48 pub id: Option<String>,
49 pub name: String,
50 pub network: String,
51 pub paused: bool,
52 pub network_type: RelayerNetworkType,
53 #[serde(skip_serializing_if = "Option::is_none")]
54 pub policies: Option<serde_json::Value>,
55 pub signer_id: String,
56 pub notification_id: Option<String>,
57 pub custom_rpc_urls: Option<Vec<RpcConfig>>,
58}
59
60impl<'de> serde::Deserialize<'de> for CreateRelayerRequest {
61 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
62 where
63 D: serde::Deserializer<'de>,
64 {
65 let raw = CreateRelayerRequestRaw::deserialize(deserializer)?;
66
67 let policies = if let Some(policies_value) = raw.policies {
69 let domain_policy =
70 deserialize_policy_for_network_type(&policies_value, raw.network_type)
71 .map_err(serde::de::Error::custom)?;
72
73 let policy = match domain_policy {
75 RelayerNetworkPolicy::Evm(evm_policy) => {
76 CreateRelayerPolicyRequest::Evm(evm_policy)
77 }
78 RelayerNetworkPolicy::Solana(solana_policy) => {
79 CreateRelayerPolicyRequest::Solana(solana_policy)
80 }
81 RelayerNetworkPolicy::Stellar(stellar_policy) => {
82 CreateRelayerPolicyRequest::Stellar(stellar_policy)
83 }
84 };
85 Some(policy)
86 } else {
87 None
88 };
89
90 Ok(CreateRelayerRequest {
91 id: raw.id,
92 name: raw.name,
93 network: raw.network,
94 paused: raw.paused,
95 network_type: raw.network_type,
96 policies,
97 signer_id: raw.signer_id,
98 notification_id: raw.notification_id,
99 custom_rpc_urls: raw.custom_rpc_urls,
100 })
101 }
102}
103
104#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, ToSchema)]
106#[serde(deny_unknown_fields, untagged)]
107pub enum CreateRelayerPolicyRequest {
108 Evm(RelayerEvmPolicy),
109 Solana(RelayerSolanaPolicy),
110 Stellar(RelayerStellarPolicy),
111}
112
113impl CreateRelayerPolicyRequest {
114 pub fn to_domain_policy(
116 &self,
117 network_type: RelayerNetworkType,
118 ) -> Result<RelayerNetworkPolicy, ApiError> {
119 match (self, network_type) {
120 (CreateRelayerPolicyRequest::Evm(policy), RelayerNetworkType::Evm) => {
121 Ok(RelayerNetworkPolicy::Evm(policy.clone()))
122 }
123 (CreateRelayerPolicyRequest::Solana(policy), RelayerNetworkType::Solana) => {
124 Ok(RelayerNetworkPolicy::Solana(policy.clone()))
125 }
126 (CreateRelayerPolicyRequest::Stellar(policy), RelayerNetworkType::Stellar) => {
127 Ok(RelayerNetworkPolicy::Stellar(policy.clone()))
128 }
129 _ => Err(ApiError::BadRequest(
130 "Policy type does not match relayer network type".to_string(),
131 )),
132 }
133 }
134}
135
136pub fn deserialize_policy_for_network_type(
139 policies_value: &serde_json::Value,
140 network_type: RelayerNetworkType,
141) -> Result<RelayerNetworkPolicy, ApiError> {
142 match network_type {
143 RelayerNetworkType::Evm => {
144 let evm_policy: RelayerEvmPolicy = serde_json::from_value(policies_value.clone())
145 .map_err(|e| ApiError::BadRequest(format!("Invalid EVM policy: {e}")))?;
146 Ok(RelayerNetworkPolicy::Evm(evm_policy))
147 }
148 RelayerNetworkType::Solana => {
149 let solana_policy: RelayerSolanaPolicy = serde_json::from_value(policies_value.clone())
150 .map_err(|e| ApiError::BadRequest(format!("Invalid Solana policy: {e}")))?;
151 Ok(RelayerNetworkPolicy::Solana(solana_policy))
152 }
153 RelayerNetworkType::Stellar => {
154 let stellar_policy: RelayerStellarPolicy =
155 serde_json::from_value(policies_value.clone())
156 .map_err(|e| ApiError::BadRequest(format!("Invalid Stellar policy: {e}")))?;
157 Ok(RelayerNetworkPolicy::Stellar(stellar_policy))
158 }
159 }
160}
161
162#[derive(Debug, Clone, Serialize, Deserialize, Default, ToSchema)]
163#[serde(deny_unknown_fields)]
164pub struct UpdateRelayerRequest {
165 pub name: Option<String>,
166 #[schema(nullable = false)]
167 pub paused: Option<bool>,
168 #[serde(skip_serializing_if = "Option::is_none")]
170 #[schema(nullable = false)]
171 pub policies: Option<CreateRelayerPolicyRequest>,
172 #[serde(skip_serializing_if = "Option::is_none")]
173 #[schema(nullable = false)]
174 pub notification_id: Option<String>,
175 pub custom_rpc_urls: Option<Vec<RpcConfig>>,
176}
177
178#[derive(Debug, Clone, Serialize, Deserialize, Default, ToSchema)]
214#[serde(deny_unknown_fields)]
215pub struct UpdateRelayerRequestRaw {
216 pub name: Option<String>,
217 pub paused: Option<bool>,
218 #[serde(skip_serializing_if = "Option::is_none")]
220 pub policies: Option<serde_json::Value>,
221 #[serde(skip_serializing_if = "Option::is_none")]
222 pub notification_id: Option<String>,
223 pub custom_rpc_urls: Option<Vec<RpcConfig>>,
224}
225
226impl TryFrom<CreateRelayerRequest> for Relayer {
227 type Error = ApiError;
228
229 fn try_from(request: CreateRelayerRequest) -> Result<Self, Self::Error> {
230 let id = request.id.clone().unwrap_or_else(generate_uuid);
231
232 let policies = if let Some(policy_request) = &request.policies {
234 Some(policy_request.to_domain_policy(request.network_type)?)
235 } else {
236 None
237 };
238
239 let relayer = Relayer::new(
241 id,
242 request.name,
243 request.network,
244 request.paused,
245 request.network_type,
246 policies,
247 request.signer_id,
248 request.notification_id,
249 request.custom_rpc_urls,
250 );
251
252 relayer.validate().map_err(ApiError::from)?;
254
255 Ok(relayer)
256 }
257}
258
259#[cfg(test)]
260mod tests {
261 use super::*;
262 use crate::models::{
263 relayer::{
264 RelayerEvmPolicy, RelayerSolanaPolicy, RelayerStellarPolicy, SolanaFeePaymentStrategy,
265 },
266 StellarFeePaymentStrategy,
267 };
268
269 #[test]
270 fn test_valid_create_request() {
271 let request = CreateRelayerRequest {
272 id: Some("test-relayer".to_string()),
273 name: "Test Relayer".to_string(),
274 network: "mainnet".to_string(),
275 paused: false,
276 network_type: RelayerNetworkType::Evm,
277 policies: Some(CreateRelayerPolicyRequest::Evm(RelayerEvmPolicy {
278 gas_price_cap: Some(100),
279 whitelist_receivers: None,
280 eip1559_pricing: Some(true),
281 private_transactions: None,
282 min_balance: None,
283 gas_limit_estimation: None,
284 })),
285 signer_id: "test-signer".to_string(),
286 notification_id: None,
287 custom_rpc_urls: None,
288 };
289
290 let domain_relayer = Relayer::try_from(request);
292 assert!(domain_relayer.is_ok());
293 }
294
295 #[test]
296 fn test_valid_create_request_stellar() {
297 let request = CreateRelayerRequest {
298 id: Some("test-stellar-relayer".to_string()),
299 name: "Test Stellar Relayer".to_string(),
300 network: "mainnet".to_string(),
301 paused: false,
302 network_type: RelayerNetworkType::Stellar,
303 policies: Some(CreateRelayerPolicyRequest::Stellar(RelayerStellarPolicy {
304 min_balance: Some(20000000),
305 max_fee: Some(100000),
306 timeout_seconds: Some(30),
307 concurrent_transactions: None,
308 allowed_tokens: None,
309 fee_payment_strategy: Some(StellarFeePaymentStrategy::Relayer),
310 slippage_percentage: None,
311 fee_margin_percentage: None,
312 swap_config: None,
313 })),
314 signer_id: "test-signer".to_string(),
315 notification_id: None,
316 custom_rpc_urls: None,
317 };
318
319 let domain_relayer = Relayer::try_from(request);
321 assert!(domain_relayer.is_ok());
322
323 let relayer = domain_relayer.unwrap();
325 assert_eq!(relayer.network_type, RelayerNetworkType::Stellar);
326 if let Some(RelayerNetworkPolicy::Stellar(stellar_policy)) = relayer.policies {
327 assert_eq!(stellar_policy.min_balance, Some(20000000));
328 assert_eq!(stellar_policy.max_fee, Some(100000));
329 assert_eq!(stellar_policy.timeout_seconds, Some(30));
330 } else {
331 panic!("Expected Stellar policy");
332 }
333 }
334
335 #[test]
336 fn test_valid_create_request_solana() {
337 let request = CreateRelayerRequest {
338 id: Some("test-solana-relayer".to_string()),
339 name: "Test Solana Relayer".to_string(),
340 network: "mainnet".to_string(),
341 paused: false,
342 network_type: RelayerNetworkType::Solana,
343 policies: Some(CreateRelayerPolicyRequest::Solana(RelayerSolanaPolicy {
344 fee_payment_strategy: Some(SolanaFeePaymentStrategy::Relayer),
345 min_balance: Some(1000000),
346 max_signatures: Some(5),
347 allowed_tokens: None,
348 allowed_programs: None,
349 allowed_accounts: None,
350 disallowed_accounts: None,
351 max_tx_data_size: None,
352 max_allowed_fee_lamports: None,
353 swap_config: None,
354 fee_margin_percentage: None,
355 })),
356 signer_id: "test-signer".to_string(),
357 notification_id: None,
358 custom_rpc_urls: None,
359 };
360
361 let domain_relayer = Relayer::try_from(request);
363 assert!(domain_relayer.is_ok());
364
365 let relayer = domain_relayer.unwrap();
367 assert_eq!(relayer.network_type, RelayerNetworkType::Solana);
368 if let Some(RelayerNetworkPolicy::Solana(solana_policy)) = relayer.policies {
369 assert_eq!(solana_policy.min_balance, Some(1000000));
370 assert_eq!(solana_policy.max_signatures, Some(5));
371 assert_eq!(
372 solana_policy.fee_payment_strategy,
373 Some(SolanaFeePaymentStrategy::Relayer)
374 );
375 } else {
376 panic!("Expected Solana policy");
377 }
378 }
379
380 #[test]
381 fn test_invalid_create_request_empty_id() {
382 let request = CreateRelayerRequest {
383 id: Some("".to_string()),
384 name: "Test Relayer".to_string(),
385 network: "mainnet".to_string(),
386 paused: false,
387 network_type: RelayerNetworkType::Evm,
388 policies: None,
389 signer_id: "test-signer".to_string(),
390 notification_id: None,
391 custom_rpc_urls: None,
392 };
393
394 let domain_relayer = Relayer::try_from(request);
396 assert!(domain_relayer.is_err());
397 }
398
399 #[test]
400 fn test_create_request_policy_conversion() {
401 let request = CreateRelayerRequest {
403 id: Some("test-relayer".to_string()),
404 name: "Test Relayer".to_string(),
405 network: "mainnet".to_string(),
406 paused: false,
407 network_type: RelayerNetworkType::Solana,
408 policies: Some(CreateRelayerPolicyRequest::Solana(RelayerSolanaPolicy {
409 fee_payment_strategy: Some(
410 crate::models::relayer::SolanaFeePaymentStrategy::Relayer,
411 ),
412 min_balance: Some(1000000),
413 allowed_tokens: None,
414 allowed_programs: None,
415 allowed_accounts: None,
416 disallowed_accounts: None,
417 max_signatures: None,
418 max_tx_data_size: None,
419 max_allowed_fee_lamports: None,
420 swap_config: None,
421 fee_margin_percentage: None,
422 })),
423 signer_id: "test-signer".to_string(),
424 notification_id: None,
425 custom_rpc_urls: None,
426 };
427
428 if let Some(policy_request) = &request.policies {
430 let policy = policy_request
431 .to_domain_policy(request.network_type)
432 .unwrap();
433 if let RelayerNetworkPolicy::Solana(solana_policy) = policy {
434 assert_eq!(solana_policy.min_balance, Some(1000000));
435 } else {
436 panic!("Expected Solana policy");
437 }
438 } else {
439 panic!("Expected policies to be present");
440 }
441
442 let domain_relayer = Relayer::try_from(request);
444 assert!(domain_relayer.is_ok());
445 }
446
447 #[test]
448 fn test_create_request_stellar_policy_conversion() {
449 let request = CreateRelayerRequest {
451 id: Some("test-stellar-relayer".to_string()),
452 name: "Test Stellar Relayer".to_string(),
453 network: "mainnet".to_string(),
454 paused: false,
455 network_type: RelayerNetworkType::Stellar,
456 policies: Some(CreateRelayerPolicyRequest::Stellar(RelayerStellarPolicy {
457 min_balance: Some(50000000),
458 max_fee: Some(150000),
459 timeout_seconds: Some(60),
460 concurrent_transactions: None,
461 allowed_tokens: None,
462 fee_payment_strategy: Some(StellarFeePaymentStrategy::Relayer),
463 slippage_percentage: None,
464 fee_margin_percentage: None,
465 swap_config: None,
466 })),
467 signer_id: "test-signer".to_string(),
468 notification_id: None,
469 custom_rpc_urls: None,
470 };
471
472 if let Some(policy_request) = &request.policies {
474 let policy = policy_request
475 .to_domain_policy(request.network_type)
476 .unwrap();
477 if let RelayerNetworkPolicy::Stellar(stellar_policy) = policy {
478 assert_eq!(stellar_policy.min_balance, Some(50000000));
479 assert_eq!(stellar_policy.max_fee, Some(150000));
480 assert_eq!(stellar_policy.timeout_seconds, Some(60));
481 } else {
482 panic!("Expected Stellar policy");
483 }
484 } else {
485 panic!("Expected policies to be present");
486 }
487
488 let domain_relayer = Relayer::try_from(request);
490 assert!(domain_relayer.is_ok());
491 }
492
493 #[test]
494 fn test_create_request_wrong_policy_type() {
495 let request = CreateRelayerRequest {
497 id: Some("test-relayer".to_string()),
498 name: "Test Relayer".to_string(),
499 network: "mainnet".to_string(),
500 paused: false,
501 network_type: RelayerNetworkType::Evm, policies: Some(CreateRelayerPolicyRequest::Solana(
503 RelayerSolanaPolicy::default(),
504 )), signer_id: "test-signer".to_string(),
506 notification_id: None,
507 custom_rpc_urls: None,
508 };
509
510 if let Some(policy_request) = &request.policies {
513 let result = policy_request.to_domain_policy(request.network_type);
514 assert!(result.is_err());
515 assert!(result
516 .unwrap_err()
517 .to_string()
518 .contains("Policy type does not match relayer network type"));
519 } else {
520 panic!("Expected policies to be present");
521 }
522 }
523
524 #[test]
525 fn test_create_request_stellar_wrong_policy_type() {
526 let request = CreateRelayerRequest {
528 id: Some("test-relayer".to_string()),
529 name: "Test Relayer".to_string(),
530 network: "mainnet".to_string(),
531 paused: false,
532 network_type: RelayerNetworkType::Evm, policies: Some(CreateRelayerPolicyRequest::Stellar(
534 RelayerStellarPolicy::default(),
535 )), signer_id: "test-signer".to_string(),
537 notification_id: None,
538 custom_rpc_urls: None,
539 };
540
541 if let Some(policy_request) = &request.policies {
543 let result = policy_request.to_domain_policy(request.network_type);
544 assert!(result.is_err());
545 assert!(result
546 .unwrap_err()
547 .to_string()
548 .contains("Policy type does not match relayer network type"));
549 } else {
550 panic!("Expected policies to be present");
551 }
552 }
553
554 #[test]
555 fn test_create_request_json_deserialization() {
556 let json_input = r#"{
558 "name": "Test Relayer",
559 "network": "mainnet",
560 "paused": false,
561 "network_type": "evm",
562 "signer_id": "test-signer",
563 "policies": {
564 "gas_price_cap": 100000000000,
565 "eip1559_pricing": true,
566 "min_balance": 1000000000000000000
567 }
568 }"#;
569
570 let request: CreateRelayerRequest = serde_json::from_str(json_input).unwrap();
571 assert_eq!(request.network_type, RelayerNetworkType::Evm);
572 assert!(request.policies.is_some());
573
574 let domain_relayer = Relayer::try_from(request).unwrap();
576 assert_eq!(domain_relayer.network_type, RelayerNetworkType::Evm);
577
578 if let Some(RelayerNetworkPolicy::Evm(evm_policy)) = domain_relayer.policies {
579 assert_eq!(evm_policy.gas_price_cap, Some(100000000000));
580 assert_eq!(evm_policy.eip1559_pricing, Some(true));
581 } else {
582 panic!("Expected EVM policy");
583 }
584 }
585
586 #[test]
587 fn test_create_request_stellar_json_deserialization() {
588 let json_input = r#"{
590 "name": "Test Stellar Relayer",
591 "network": "mainnet",
592 "paused": false,
593 "network_type": "stellar",
594 "signer_id": "test-signer",
595 "policies": {
596 "fee_payment_strategy": "relayer",
597 "min_balance": 25000000,
598 "max_fee": 200000,
599 "timeout_seconds": 45
600 }
601 }"#;
602
603 let request: CreateRelayerRequest = serde_json::from_str(json_input).unwrap();
604 assert_eq!(request.network_type, RelayerNetworkType::Stellar);
605 assert!(request.policies.is_some());
606
607 let domain_relayer = Relayer::try_from(request).unwrap();
609 assert_eq!(domain_relayer.network_type, RelayerNetworkType::Stellar);
610
611 if let Some(RelayerNetworkPolicy::Stellar(stellar_policy)) = domain_relayer.policies {
612 assert_eq!(stellar_policy.min_balance, Some(25000000));
613 assert_eq!(stellar_policy.max_fee, Some(200000));
614 assert_eq!(stellar_policy.timeout_seconds, Some(45));
615 } else {
616 panic!("Expected Stellar policy");
617 }
618 }
619
620 #[test]
621 fn test_create_request_solana_json_deserialization() {
622 let json_input = r#"{
624 "name": "Test Solana Relayer",
625 "network": "mainnet",
626 "paused": false,
627 "network_type": "solana",
628 "signer_id": "test-signer",
629 "policies": {
630 "fee_payment_strategy": "relayer",
631 "min_balance": 5000000,
632 "max_signatures": 8,
633 "max_tx_data_size": 1024,
634 "fee_margin_percentage": 2.5
635 }
636 }"#;
637
638 let request: CreateRelayerRequest = serde_json::from_str(json_input).unwrap();
639 assert_eq!(request.network_type, RelayerNetworkType::Solana);
640 assert!(request.policies.is_some());
641
642 let domain_relayer = Relayer::try_from(request).unwrap();
644 assert_eq!(domain_relayer.network_type, RelayerNetworkType::Solana);
645
646 if let Some(RelayerNetworkPolicy::Solana(solana_policy)) = domain_relayer.policies {
647 assert_eq!(solana_policy.min_balance, Some(5000000));
648 assert_eq!(solana_policy.max_signatures, Some(8));
649 assert_eq!(solana_policy.max_tx_data_size, Some(1024));
650 assert_eq!(solana_policy.fee_margin_percentage, Some(2.5));
651 assert_eq!(
652 solana_policy.fee_payment_strategy,
653 Some(SolanaFeePaymentStrategy::Relayer)
654 );
655 } else {
656 panic!("Expected Solana policy");
657 }
658 }
659
660 #[test]
661 fn test_valid_update_request() {
662 let request = UpdateRelayerRequestRaw {
663 name: Some("Updated Name".to_string()),
664 paused: Some(true),
665 policies: None,
666 notification_id: Some("new-notification".to_string()),
667 custom_rpc_urls: None,
668 };
669
670 let serialized = serde_json::to_string(&request).unwrap();
672 let _deserialized: UpdateRelayerRequest = serde_json::from_str(&serialized).unwrap();
673 }
674
675 #[test]
676 fn test_update_request_all_none() {
677 let request = UpdateRelayerRequestRaw {
678 name: None,
679 paused: None,
680 policies: None,
681 notification_id: None,
682 custom_rpc_urls: None,
683 };
684
685 let serialized = serde_json::to_string(&request).unwrap();
687 let _deserialized: UpdateRelayerRequest = serde_json::from_str(&serialized).unwrap();
688 }
689
690 #[test]
691 fn test_update_request_policy_deserialization() {
692 let json_input = r#"{
694 "name": "Updated Relayer",
695 "policies": {
696 "gas_price_cap": 100000000000,
697 "eip1559_pricing": true
698 }
699 }"#;
700
701 let request: UpdateRelayerRequestRaw = serde_json::from_str(json_input).unwrap();
702 assert!(request.policies.is_some());
703
704 if let Some(policies_json) = &request.policies {
707 let network_policy =
708 deserialize_policy_for_network_type(policies_json, RelayerNetworkType::Evm)
709 .unwrap();
710 if let RelayerNetworkPolicy::Evm(evm_policy) = network_policy {
711 assert_eq!(evm_policy.gas_price_cap, Some(100000000000));
712 assert_eq!(evm_policy.eip1559_pricing, Some(true));
713 } else {
714 panic!("Expected EVM policy");
715 }
716 }
717 }
718
719 #[test]
720 fn test_update_request_policy_deserialization_solana() {
721 let json_input = r#"{
723 "policies": {
724 "fee_payment_strategy": "relayer",
725 "min_balance": 1000000
726 }
727 }"#;
728
729 let request: UpdateRelayerRequestRaw = serde_json::from_str(json_input).unwrap();
730
731 if let Some(policies_json) = &request.policies {
734 let network_policy =
735 deserialize_policy_for_network_type(policies_json, RelayerNetworkType::Solana)
736 .unwrap();
737 if let RelayerNetworkPolicy::Solana(solana_policy) = network_policy {
738 assert_eq!(solana_policy.min_balance, Some(1000000));
739 } else {
740 panic!("Expected Solana policy");
741 }
742 }
743 }
744
745 #[test]
746 fn test_update_request_policy_deserialization_stellar() {
747 let json_input = r#"{
749 "policies": {
750 "max_fee": 75000,
751 "timeout_seconds": 120,
752 "min_balance": 15000000
753 }
754 }"#;
755
756 let request: UpdateRelayerRequestRaw = serde_json::from_str(json_input).unwrap();
757
758 if let Some(policies_json) = &request.policies {
761 let network_policy =
762 deserialize_policy_for_network_type(policies_json, RelayerNetworkType::Stellar)
763 .unwrap();
764 if let RelayerNetworkPolicy::Stellar(stellar_policy) = network_policy {
765 assert_eq!(stellar_policy.max_fee, Some(75000));
766 assert_eq!(stellar_policy.timeout_seconds, Some(120));
767 assert_eq!(stellar_policy.min_balance, Some(15000000));
768 } else {
769 panic!("Expected Stellar policy");
770 }
771 }
772 }
773
774 #[test]
775 fn test_update_request_invalid_policy_format() {
776 let valid_json = r#"{
778 "name": "Test",
779 "policies": "invalid_not_an_object"
780 }"#;
781
782 let request: UpdateRelayerRequestRaw = serde_json::from_str(valid_json).unwrap();
783
784 if let Some(policies_json) = &request.policies {
786 let result =
787 deserialize_policy_for_network_type(policies_json, RelayerNetworkType::Evm);
788 assert!(result.is_err());
789 }
790 }
791
792 #[test]
793 fn test_update_request_wrong_network_type() {
794 let json_input = r#"{
796 "policies": {
797 "gas_price_cap": 100000000000,
798 "eip1559_pricing": true
799 }
800 }"#;
801
802 let request: UpdateRelayerRequestRaw = serde_json::from_str(json_input).unwrap();
803
804 assert!(request.policies.is_some());
806 }
807
808 #[test]
809 fn test_update_request_stellar_policy() {
810 let json_input = r#"{
812 "policies": {
813 "max_fee": 10000,
814 "timeout_seconds": 300,
815 "min_balance": 5000000
816 }
817 }"#;
818
819 let request: UpdateRelayerRequestRaw = serde_json::from_str(json_input).unwrap();
820
821 assert!(request.policies.is_some());
823 }
824
825 #[test]
826 fn test_update_request_stellar_policy_partial() {
827 let json_input = r#"{
829 "policies": {
830 "max_fee": 50000
831 }
832 }"#;
833
834 let request: UpdateRelayerRequestRaw = serde_json::from_str(json_input).unwrap();
835
836 assert!(request.policies.is_some());
838
839 if let Some(policies_json) = &request.policies {
841 let network_policy =
842 deserialize_policy_for_network_type(policies_json, RelayerNetworkType::Stellar)
843 .unwrap();
844 if let RelayerNetworkPolicy::Stellar(stellar_policy) = network_policy {
845 assert_eq!(stellar_policy.max_fee, Some(50000));
846 assert_eq!(stellar_policy.timeout_seconds, None);
847 assert_eq!(stellar_policy.min_balance, None);
848 } else {
849 panic!("Expected Stellar policy");
850 }
851 }
852 }
853
854 #[test]
855 fn test_notification_id_deserialization() {
856 let json_with_notification = r#"{
858 "name": "Test Relayer",
859 "notification_id": "notif-123"
860 }"#;
861
862 let request: UpdateRelayerRequestRaw =
863 serde_json::from_str(json_with_notification).unwrap();
864 assert_eq!(request.notification_id, Some("notif-123".to_string()));
865
866 let json_without_notification = r#"{
868 "name": "Test Relayer"
869 }"#;
870
871 let request: UpdateRelayerRequestRaw =
872 serde_json::from_str(json_without_notification).unwrap();
873 assert_eq!(request.notification_id, None);
874
875 let invalid_json = r#"{
877 "name": "Test Relayer",
878 "notification_id": 123
879 }"#;
880
881 let result = serde_json::from_str::<UpdateRelayerRequestRaw>(invalid_json);
882 assert!(result.is_err());
883 }
884
885 #[test]
886 fn test_comprehensive_update_request() {
887 let json_input = r#"{
889 "name": "Updated Relayer",
890 "paused": true,
891 "notification_id": "new-notification-id",
892 "policies": {
893 "min_balance": "5000000000000000000",
894 "gas_limit_estimation": false
895 },
896 "custom_rpc_urls": [
897 {"url": "https://example.com", "weight": 100}
898 ]
899 }"#;
900
901 let request: UpdateRelayerRequestRaw = serde_json::from_str(json_input).unwrap();
902
903 assert_eq!(request.name, Some("Updated Relayer".to_string()));
905 assert_eq!(request.paused, Some(true));
906 assert_eq!(
907 request.notification_id,
908 Some("new-notification-id".to_string())
909 );
910 assert!(request.policies.is_some());
911 assert!(request.custom_rpc_urls.is_some());
912
913 if let Some(policies_json) = &request.policies {
915 assert!(policies_json.get("min_balance").is_some());
917 assert!(policies_json.get("gas_limit_estimation").is_some());
918 } else {
919 panic!("Expected policies");
920 }
921 }
922
923 #[test]
924 fn test_comprehensive_update_request_stellar() {
925 let json_input = r#"{
927 "name": "Updated Stellar Relayer",
928 "paused": false,
929 "notification_id": "stellar-notification",
930 "policies": {
931 "min_balance": 30000000,
932 "max_fee": 250000,
933 "timeout_seconds": 90
934 },
935 "custom_rpc_urls": [
936 {"url": "https://stellar-node.example.com", "weight": 100}
937 ]
938 }"#;
939
940 let request: UpdateRelayerRequestRaw = serde_json::from_str(json_input).unwrap();
941
942 assert_eq!(request.name, Some("Updated Stellar Relayer".to_string()));
944 assert_eq!(request.paused, Some(false));
945 assert_eq!(
946 request.notification_id,
947 Some("stellar-notification".to_string())
948 );
949 assert!(request.policies.is_some());
950 assert!(request.custom_rpc_urls.is_some());
951
952 if let Some(policies_json) = &request.policies {
954 let network_policy =
955 deserialize_policy_for_network_type(policies_json, RelayerNetworkType::Stellar)
956 .unwrap();
957 if let RelayerNetworkPolicy::Stellar(stellar_policy) = network_policy {
958 assert_eq!(stellar_policy.min_balance, Some(30000000));
959 assert_eq!(stellar_policy.max_fee, Some(250000));
960 assert_eq!(stellar_policy.timeout_seconds, Some(90));
961 } else {
962 panic!("Expected Stellar policy");
963 }
964 }
965 }
966
967 #[test]
968 fn test_create_request_network_type_based_policy_deserialization() {
969 let evm_json = r#"{
972 "name": "EVM Relayer",
973 "network": "mainnet",
974 "paused": false,
975 "network_type": "evm",
976 "signer_id": "test-signer",
977 "policies": {
978 "gas_price_cap": 50000000000,
979 "eip1559_pricing": true,
980 "min_balance": "1000000000000000000"
981 }
982 }"#;
983
984 let evm_request: CreateRelayerRequest = serde_json::from_str(evm_json).unwrap();
985 assert_eq!(evm_request.network_type, RelayerNetworkType::Evm);
986
987 if let Some(CreateRelayerPolicyRequest::Evm(evm_policy)) = evm_request.policies {
988 assert_eq!(evm_policy.gas_price_cap, Some(50000000000));
989 assert_eq!(evm_policy.eip1559_pricing, Some(true));
990 assert_eq!(evm_policy.min_balance, Some(1000000000000000000));
991 } else {
992 panic!("Expected EVM policy");
993 }
994
995 let solana_json = r#"{
997 "name": "Solana Relayer",
998 "network": "mainnet",
999 "paused": false,
1000 "network_type": "solana",
1001 "signer_id": "test-signer",
1002 "policies": {
1003 "fee_payment_strategy": "relayer",
1004 "min_balance": 5000000,
1005 "max_signatures": 10
1006 }
1007 }"#;
1008
1009 let solana_request: CreateRelayerRequest = serde_json::from_str(solana_json).unwrap();
1010 assert_eq!(solana_request.network_type, RelayerNetworkType::Solana);
1011
1012 if let Some(CreateRelayerPolicyRequest::Solana(solana_policy)) = solana_request.policies {
1013 assert_eq!(solana_policy.min_balance, Some(5000000));
1014 assert_eq!(solana_policy.max_signatures, Some(10));
1015 } else {
1016 panic!("Expected Solana policy");
1017 }
1018
1019 let stellar_json = r#"{
1021 "name": "Stellar Relayer",
1022 "network": "mainnet",
1023 "paused": false,
1024 "network_type": "stellar",
1025 "signer_id": "test-signer",
1026 "policies": {
1027 "min_balance": 40000000,
1028 "max_fee": 300000,
1029 "timeout_seconds": 180
1030 }
1031 }"#;
1032
1033 let stellar_request: CreateRelayerRequest = serde_json::from_str(stellar_json).unwrap();
1034 assert_eq!(stellar_request.network_type, RelayerNetworkType::Stellar);
1035
1036 if let Some(CreateRelayerPolicyRequest::Stellar(stellar_policy)) = stellar_request.policies
1037 {
1038 assert_eq!(stellar_policy.min_balance, Some(40000000));
1039 assert_eq!(stellar_policy.max_fee, Some(300000));
1040 assert_eq!(stellar_policy.timeout_seconds, Some(180));
1041 } else {
1042 panic!("Expected Stellar policy");
1043 }
1044
1045 let invalid_json = r#"{
1047 "name": "Invalid Relayer",
1048 "network": "mainnet",
1049 "paused": false,
1050 "network_type": "evm",
1051 "signer_id": "test-signer",
1052 "policies": {
1053 "fee_payment_strategy": "relayer"
1054 }
1055 }"#;
1056
1057 let result = serde_json::from_str::<CreateRelayerRequest>(invalid_json);
1058 assert!(result.is_err());
1059 assert!(result.unwrap_err().to_string().contains("unknown field"));
1060 }
1061
1062 #[test]
1063 fn test_create_request_invalid_stellar_policy_fields() {
1064 let invalid_json = r#"{
1066 "name": "Invalid Stellar Relayer",
1067 "network": "mainnet",
1068 "paused": false,
1069 "network_type": "stellar",
1070 "signer_id": "test-signer",
1071 "policies": {
1072 "gas_price_cap": 100000000000
1073 }
1074 }"#;
1075
1076 let result = serde_json::from_str::<CreateRelayerRequest>(invalid_json);
1077 assert!(result.is_err());
1078 assert!(result.unwrap_err().to_string().contains("unknown field"));
1079 }
1080
1081 #[test]
1082 fn test_create_request_empty_policies() {
1083 let evm_json = r#"{
1085 "name": "EVM Relayer No Policies",
1086 "network": "mainnet",
1087 "paused": false,
1088 "network_type": "evm",
1089 "signer_id": "test-signer"
1090 }"#;
1091
1092 let evm_request: CreateRelayerRequest = serde_json::from_str(evm_json).unwrap();
1093 assert_eq!(evm_request.network_type, RelayerNetworkType::Evm);
1094 assert!(evm_request.policies.is_none());
1095
1096 let stellar_json = r#"{
1097 "name": "Stellar Relayer No Policies",
1098 "network": "mainnet",
1099 "paused": false,
1100 "network_type": "stellar",
1101 "signer_id": "test-signer"
1102 }"#;
1103
1104 let stellar_request: CreateRelayerRequest = serde_json::from_str(stellar_json).unwrap();
1105 assert_eq!(stellar_request.network_type, RelayerNetworkType::Stellar);
1106 assert!(stellar_request.policies.is_none());
1107
1108 let solana_json = r#"{
1109 "name": "Solana Relayer No Policies",
1110 "network": "mainnet",
1111 "paused": false,
1112 "network_type": "solana",
1113 "signer_id": "test-signer"
1114 }"#;
1115
1116 let solana_request: CreateRelayerRequest = serde_json::from_str(solana_json).unwrap();
1117 assert_eq!(solana_request.network_type, RelayerNetworkType::Solana);
1118 assert!(solana_request.policies.is_none());
1119 }
1120
1121 #[test]
1122 fn test_deserialize_policy_utility_function_all_networks() {
1123 let evm_json = serde_json::json!({
1127 "gas_price_cap": "75000000000",
1128 "private_transactions": false,
1129 "min_balance": "2000000000000000000"
1130 });
1131
1132 let evm_policy =
1133 deserialize_policy_for_network_type(&evm_json, RelayerNetworkType::Evm).unwrap();
1134 if let RelayerNetworkPolicy::Evm(policy) = evm_policy {
1135 assert_eq!(policy.gas_price_cap, Some(75000000000));
1136 assert_eq!(policy.private_transactions, Some(false));
1137 assert_eq!(policy.min_balance, Some(2000000000000000000));
1138 } else {
1139 panic!("Expected EVM policy");
1140 }
1141
1142 let solana_json = serde_json::json!({
1144 "fee_payment_strategy": "user",
1145 "max_tx_data_size": 512,
1146 "fee_margin_percentage": 1.5
1147 });
1148
1149 let solana_policy =
1150 deserialize_policy_for_network_type(&solana_json, RelayerNetworkType::Solana).unwrap();
1151 if let RelayerNetworkPolicy::Solana(policy) = solana_policy {
1152 assert_eq!(
1153 policy.fee_payment_strategy,
1154 Some(SolanaFeePaymentStrategy::User)
1155 );
1156 assert_eq!(policy.max_tx_data_size, Some(512));
1157 assert_eq!(policy.fee_margin_percentage, Some(1.5));
1158 } else {
1159 panic!("Expected Solana policy");
1160 }
1161
1162 let stellar_json = serde_json::json!({
1164 "max_fee": 125000,
1165 "timeout_seconds": 240
1166 });
1167
1168 let stellar_policy =
1169 deserialize_policy_for_network_type(&stellar_json, RelayerNetworkType::Stellar)
1170 .unwrap();
1171 if let RelayerNetworkPolicy::Stellar(policy) = stellar_policy {
1172 assert_eq!(policy.max_fee, Some(125000));
1173 assert_eq!(policy.timeout_seconds, Some(240));
1174 assert_eq!(policy.min_balance, None);
1175 } else {
1176 panic!("Expected Stellar policy");
1177 }
1178 }
1179}