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)]
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::relayer::{
263 RelayerEvmPolicy, RelayerSolanaPolicy, RelayerStellarPolicy, SolanaFeePaymentStrategy,
264 };
265
266 #[test]
267 fn test_valid_create_request() {
268 let request = CreateRelayerRequest {
269 id: Some("test-relayer".to_string()),
270 name: "Test Relayer".to_string(),
271 network: "mainnet".to_string(),
272 paused: false,
273 network_type: RelayerNetworkType::Evm,
274 policies: Some(CreateRelayerPolicyRequest::Evm(RelayerEvmPolicy {
275 gas_price_cap: Some(100),
276 whitelist_receivers: None,
277 eip1559_pricing: Some(true),
278 private_transactions: None,
279 min_balance: None,
280 gas_limit_estimation: None,
281 })),
282 signer_id: "test-signer".to_string(),
283 notification_id: None,
284 custom_rpc_urls: None,
285 };
286
287 let domain_relayer = Relayer::try_from(request);
289 assert!(domain_relayer.is_ok());
290 }
291
292 #[test]
293 fn test_valid_create_request_stellar() {
294 let request = CreateRelayerRequest {
295 id: Some("test-stellar-relayer".to_string()),
296 name: "Test Stellar Relayer".to_string(),
297 network: "mainnet".to_string(),
298 paused: false,
299 network_type: RelayerNetworkType::Stellar,
300 policies: Some(CreateRelayerPolicyRequest::Stellar(RelayerStellarPolicy {
301 min_balance: Some(20000000),
302 max_fee: Some(100000),
303 timeout_seconds: Some(30),
304 concurrent_transactions: None,
305 })),
306 signer_id: "test-signer".to_string(),
307 notification_id: None,
308 custom_rpc_urls: None,
309 };
310
311 let domain_relayer = Relayer::try_from(request);
313 assert!(domain_relayer.is_ok());
314
315 let relayer = domain_relayer.unwrap();
317 assert_eq!(relayer.network_type, RelayerNetworkType::Stellar);
318 if let Some(RelayerNetworkPolicy::Stellar(stellar_policy)) = relayer.policies {
319 assert_eq!(stellar_policy.min_balance, Some(20000000));
320 assert_eq!(stellar_policy.max_fee, Some(100000));
321 assert_eq!(stellar_policy.timeout_seconds, Some(30));
322 } else {
323 panic!("Expected Stellar policy");
324 }
325 }
326
327 #[test]
328 fn test_valid_create_request_solana() {
329 let request = CreateRelayerRequest {
330 id: Some("test-solana-relayer".to_string()),
331 name: "Test Solana Relayer".to_string(),
332 network: "mainnet".to_string(),
333 paused: false,
334 network_type: RelayerNetworkType::Solana,
335 policies: Some(CreateRelayerPolicyRequest::Solana(RelayerSolanaPolicy {
336 fee_payment_strategy: Some(SolanaFeePaymentStrategy::Relayer),
337 min_balance: Some(1000000),
338 max_signatures: Some(5),
339 allowed_tokens: None,
340 allowed_programs: None,
341 allowed_accounts: None,
342 disallowed_accounts: None,
343 max_tx_data_size: None,
344 max_allowed_fee_lamports: None,
345 swap_config: None,
346 fee_margin_percentage: None,
347 })),
348 signer_id: "test-signer".to_string(),
349 notification_id: None,
350 custom_rpc_urls: None,
351 };
352
353 let domain_relayer = Relayer::try_from(request);
355 assert!(domain_relayer.is_ok());
356
357 let relayer = domain_relayer.unwrap();
359 assert_eq!(relayer.network_type, RelayerNetworkType::Solana);
360 if let Some(RelayerNetworkPolicy::Solana(solana_policy)) = relayer.policies {
361 assert_eq!(solana_policy.min_balance, Some(1000000));
362 assert_eq!(solana_policy.max_signatures, Some(5));
363 assert_eq!(
364 solana_policy.fee_payment_strategy,
365 Some(SolanaFeePaymentStrategy::Relayer)
366 );
367 } else {
368 panic!("Expected Solana policy");
369 }
370 }
371
372 #[test]
373 fn test_invalid_create_request_empty_id() {
374 let request = CreateRelayerRequest {
375 id: Some("".to_string()),
376 name: "Test Relayer".to_string(),
377 network: "mainnet".to_string(),
378 paused: false,
379 network_type: RelayerNetworkType::Evm,
380 policies: None,
381 signer_id: "test-signer".to_string(),
382 notification_id: None,
383 custom_rpc_urls: None,
384 };
385
386 let domain_relayer = Relayer::try_from(request);
388 assert!(domain_relayer.is_err());
389 }
390
391 #[test]
392 fn test_create_request_policy_conversion() {
393 let request = CreateRelayerRequest {
395 id: Some("test-relayer".to_string()),
396 name: "Test Relayer".to_string(),
397 network: "mainnet".to_string(),
398 paused: false,
399 network_type: RelayerNetworkType::Solana,
400 policies: Some(CreateRelayerPolicyRequest::Solana(RelayerSolanaPolicy {
401 fee_payment_strategy: Some(
402 crate::models::relayer::SolanaFeePaymentStrategy::Relayer,
403 ),
404 min_balance: Some(1000000),
405 allowed_tokens: None,
406 allowed_programs: None,
407 allowed_accounts: None,
408 disallowed_accounts: None,
409 max_signatures: None,
410 max_tx_data_size: None,
411 max_allowed_fee_lamports: None,
412 swap_config: None,
413 fee_margin_percentage: None,
414 })),
415 signer_id: "test-signer".to_string(),
416 notification_id: None,
417 custom_rpc_urls: None,
418 };
419
420 if let Some(policy_request) = &request.policies {
422 let policy = policy_request
423 .to_domain_policy(request.network_type)
424 .unwrap();
425 if let RelayerNetworkPolicy::Solana(solana_policy) = policy {
426 assert_eq!(solana_policy.min_balance, Some(1000000));
427 } else {
428 panic!("Expected Solana policy");
429 }
430 } else {
431 panic!("Expected policies to be present");
432 }
433
434 let domain_relayer = Relayer::try_from(request);
436 assert!(domain_relayer.is_ok());
437 }
438
439 #[test]
440 fn test_create_request_stellar_policy_conversion() {
441 let request = CreateRelayerRequest {
443 id: Some("test-stellar-relayer".to_string()),
444 name: "Test Stellar Relayer".to_string(),
445 network: "mainnet".to_string(),
446 paused: false,
447 network_type: RelayerNetworkType::Stellar,
448 policies: Some(CreateRelayerPolicyRequest::Stellar(RelayerStellarPolicy {
449 min_balance: Some(50000000),
450 max_fee: Some(150000),
451 timeout_seconds: Some(60),
452 concurrent_transactions: None,
453 })),
454 signer_id: "test-signer".to_string(),
455 notification_id: None,
456 custom_rpc_urls: None,
457 };
458
459 if let Some(policy_request) = &request.policies {
461 let policy = policy_request
462 .to_domain_policy(request.network_type)
463 .unwrap();
464 if let RelayerNetworkPolicy::Stellar(stellar_policy) = policy {
465 assert_eq!(stellar_policy.min_balance, Some(50000000));
466 assert_eq!(stellar_policy.max_fee, Some(150000));
467 assert_eq!(stellar_policy.timeout_seconds, Some(60));
468 } else {
469 panic!("Expected Stellar policy");
470 }
471 } else {
472 panic!("Expected policies to be present");
473 }
474
475 let domain_relayer = Relayer::try_from(request);
477 assert!(domain_relayer.is_ok());
478 }
479
480 #[test]
481 fn test_create_request_wrong_policy_type() {
482 let request = CreateRelayerRequest {
484 id: Some("test-relayer".to_string()),
485 name: "Test Relayer".to_string(),
486 network: "mainnet".to_string(),
487 paused: false,
488 network_type: RelayerNetworkType::Evm, policies: Some(CreateRelayerPolicyRequest::Solana(
490 RelayerSolanaPolicy::default(),
491 )), signer_id: "test-signer".to_string(),
493 notification_id: None,
494 custom_rpc_urls: None,
495 };
496
497 if let Some(policy_request) = &request.policies {
500 let result = policy_request.to_domain_policy(request.network_type);
501 assert!(result.is_err());
502 assert!(result
503 .unwrap_err()
504 .to_string()
505 .contains("Policy type does not match relayer network type"));
506 } else {
507 panic!("Expected policies to be present");
508 }
509 }
510
511 #[test]
512 fn test_create_request_stellar_wrong_policy_type() {
513 let request = CreateRelayerRequest {
515 id: Some("test-relayer".to_string()),
516 name: "Test Relayer".to_string(),
517 network: "mainnet".to_string(),
518 paused: false,
519 network_type: RelayerNetworkType::Evm, policies: Some(CreateRelayerPolicyRequest::Stellar(
521 RelayerStellarPolicy::default(),
522 )), signer_id: "test-signer".to_string(),
524 notification_id: None,
525 custom_rpc_urls: None,
526 };
527
528 if let Some(policy_request) = &request.policies {
530 let result = policy_request.to_domain_policy(request.network_type);
531 assert!(result.is_err());
532 assert!(result
533 .unwrap_err()
534 .to_string()
535 .contains("Policy type does not match relayer network type"));
536 } else {
537 panic!("Expected policies to be present");
538 }
539 }
540
541 #[test]
542 fn test_create_request_json_deserialization() {
543 let json_input = r#"{
545 "name": "Test Relayer",
546 "network": "mainnet",
547 "paused": false,
548 "network_type": "evm",
549 "signer_id": "test-signer",
550 "policies": {
551 "gas_price_cap": 100000000000,
552 "eip1559_pricing": true,
553 "min_balance": 1000000000000000000
554 }
555 }"#;
556
557 let request: CreateRelayerRequest = serde_json::from_str(json_input).unwrap();
558 assert_eq!(request.network_type, RelayerNetworkType::Evm);
559 assert!(request.policies.is_some());
560
561 let domain_relayer = Relayer::try_from(request).unwrap();
563 assert_eq!(domain_relayer.network_type, RelayerNetworkType::Evm);
564
565 if let Some(RelayerNetworkPolicy::Evm(evm_policy)) = domain_relayer.policies {
566 assert_eq!(evm_policy.gas_price_cap, Some(100000000000));
567 assert_eq!(evm_policy.eip1559_pricing, Some(true));
568 } else {
569 panic!("Expected EVM policy");
570 }
571 }
572
573 #[test]
574 fn test_create_request_stellar_json_deserialization() {
575 let json_input = r#"{
577 "name": "Test Stellar Relayer",
578 "network": "mainnet",
579 "paused": false,
580 "network_type": "stellar",
581 "signer_id": "test-signer",
582 "policies": {
583 "min_balance": 25000000,
584 "max_fee": 200000,
585 "timeout_seconds": 45
586 }
587 }"#;
588
589 let request: CreateRelayerRequest = serde_json::from_str(json_input).unwrap();
590 assert_eq!(request.network_type, RelayerNetworkType::Stellar);
591 assert!(request.policies.is_some());
592
593 let domain_relayer = Relayer::try_from(request).unwrap();
595 assert_eq!(domain_relayer.network_type, RelayerNetworkType::Stellar);
596
597 if let Some(RelayerNetworkPolicy::Stellar(stellar_policy)) = domain_relayer.policies {
598 assert_eq!(stellar_policy.min_balance, Some(25000000));
599 assert_eq!(stellar_policy.max_fee, Some(200000));
600 assert_eq!(stellar_policy.timeout_seconds, Some(45));
601 } else {
602 panic!("Expected Stellar policy");
603 }
604 }
605
606 #[test]
607 fn test_create_request_solana_json_deserialization() {
608 let json_input = r#"{
610 "name": "Test Solana Relayer",
611 "network": "mainnet",
612 "paused": false,
613 "network_type": "solana",
614 "signer_id": "test-signer",
615 "policies": {
616 "fee_payment_strategy": "relayer",
617 "min_balance": 5000000,
618 "max_signatures": 8,
619 "max_tx_data_size": 1024,
620 "fee_margin_percentage": 2.5
621 }
622 }"#;
623
624 let request: CreateRelayerRequest = serde_json::from_str(json_input).unwrap();
625 assert_eq!(request.network_type, RelayerNetworkType::Solana);
626 assert!(request.policies.is_some());
627
628 let domain_relayer = Relayer::try_from(request).unwrap();
630 assert_eq!(domain_relayer.network_type, RelayerNetworkType::Solana);
631
632 if let Some(RelayerNetworkPolicy::Solana(solana_policy)) = domain_relayer.policies {
633 assert_eq!(solana_policy.min_balance, Some(5000000));
634 assert_eq!(solana_policy.max_signatures, Some(8));
635 assert_eq!(solana_policy.max_tx_data_size, Some(1024));
636 assert_eq!(solana_policy.fee_margin_percentage, Some(2.5));
637 assert_eq!(
638 solana_policy.fee_payment_strategy,
639 Some(SolanaFeePaymentStrategy::Relayer)
640 );
641 } else {
642 panic!("Expected Solana policy");
643 }
644 }
645
646 #[test]
647 fn test_valid_update_request() {
648 let request = UpdateRelayerRequestRaw {
649 name: Some("Updated Name".to_string()),
650 paused: Some(true),
651 policies: None,
652 notification_id: Some("new-notification".to_string()),
653 custom_rpc_urls: None,
654 };
655
656 let serialized = serde_json::to_string(&request).unwrap();
658 let _deserialized: UpdateRelayerRequest = serde_json::from_str(&serialized).unwrap();
659 }
660
661 #[test]
662 fn test_update_request_all_none() {
663 let request = UpdateRelayerRequestRaw {
664 name: None,
665 paused: None,
666 policies: None,
667 notification_id: None,
668 custom_rpc_urls: None,
669 };
670
671 let serialized = serde_json::to_string(&request).unwrap();
673 let _deserialized: UpdateRelayerRequest = serde_json::from_str(&serialized).unwrap();
674 }
675
676 #[test]
677 fn test_update_request_policy_deserialization() {
678 let json_input = r#"{
680 "name": "Updated Relayer",
681 "policies": {
682 "gas_price_cap": 100000000000,
683 "eip1559_pricing": true
684 }
685 }"#;
686
687 let request: UpdateRelayerRequestRaw = serde_json::from_str(json_input).unwrap();
688 assert!(request.policies.is_some());
689
690 if let Some(policies_json) = &request.policies {
693 let network_policy =
694 deserialize_policy_for_network_type(policies_json, RelayerNetworkType::Evm)
695 .unwrap();
696 if let RelayerNetworkPolicy::Evm(evm_policy) = network_policy {
697 assert_eq!(evm_policy.gas_price_cap, Some(100000000000));
698 assert_eq!(evm_policy.eip1559_pricing, Some(true));
699 } else {
700 panic!("Expected EVM policy");
701 }
702 }
703 }
704
705 #[test]
706 fn test_update_request_policy_deserialization_solana() {
707 let json_input = r#"{
709 "policies": {
710 "fee_payment_strategy": "relayer",
711 "min_balance": 1000000
712 }
713 }"#;
714
715 let request: UpdateRelayerRequestRaw = serde_json::from_str(json_input).unwrap();
716
717 if let Some(policies_json) = &request.policies {
720 let network_policy =
721 deserialize_policy_for_network_type(policies_json, RelayerNetworkType::Solana)
722 .unwrap();
723 if let RelayerNetworkPolicy::Solana(solana_policy) = network_policy {
724 assert_eq!(solana_policy.min_balance, Some(1000000));
725 } else {
726 panic!("Expected Solana policy");
727 }
728 }
729 }
730
731 #[test]
732 fn test_update_request_policy_deserialization_stellar() {
733 let json_input = r#"{
735 "policies": {
736 "max_fee": 75000,
737 "timeout_seconds": 120,
738 "min_balance": 15000000
739 }
740 }"#;
741
742 let request: UpdateRelayerRequestRaw = serde_json::from_str(json_input).unwrap();
743
744 if let Some(policies_json) = &request.policies {
747 let network_policy =
748 deserialize_policy_for_network_type(policies_json, RelayerNetworkType::Stellar)
749 .unwrap();
750 if let RelayerNetworkPolicy::Stellar(stellar_policy) = network_policy {
751 assert_eq!(stellar_policy.max_fee, Some(75000));
752 assert_eq!(stellar_policy.timeout_seconds, Some(120));
753 assert_eq!(stellar_policy.min_balance, Some(15000000));
754 } else {
755 panic!("Expected Stellar policy");
756 }
757 }
758 }
759
760 #[test]
761 fn test_update_request_invalid_policy_format() {
762 let valid_json = r#"{
764 "name": "Test",
765 "policies": "invalid_not_an_object"
766 }"#;
767
768 let request: UpdateRelayerRequestRaw = serde_json::from_str(valid_json).unwrap();
769
770 if let Some(policies_json) = &request.policies {
772 let result =
773 deserialize_policy_for_network_type(policies_json, RelayerNetworkType::Evm);
774 assert!(result.is_err());
775 }
776 }
777
778 #[test]
779 fn test_update_request_wrong_network_type() {
780 let json_input = r#"{
782 "policies": {
783 "gas_price_cap": 100000000000,
784 "eip1559_pricing": true
785 }
786 }"#;
787
788 let request: UpdateRelayerRequestRaw = serde_json::from_str(json_input).unwrap();
789
790 assert!(request.policies.is_some());
792 }
793
794 #[test]
795 fn test_update_request_stellar_policy() {
796 let json_input = r#"{
798 "policies": {
799 "max_fee": 10000,
800 "timeout_seconds": 300,
801 "min_balance": 5000000
802 }
803 }"#;
804
805 let request: UpdateRelayerRequestRaw = serde_json::from_str(json_input).unwrap();
806
807 assert!(request.policies.is_some());
809 }
810
811 #[test]
812 fn test_update_request_stellar_policy_partial() {
813 let json_input = r#"{
815 "policies": {
816 "max_fee": 50000
817 }
818 }"#;
819
820 let request: UpdateRelayerRequestRaw = serde_json::from_str(json_input).unwrap();
821
822 assert!(request.policies.is_some());
824
825 if let Some(policies_json) = &request.policies {
827 let network_policy =
828 deserialize_policy_for_network_type(policies_json, RelayerNetworkType::Stellar)
829 .unwrap();
830 if let RelayerNetworkPolicy::Stellar(stellar_policy) = network_policy {
831 assert_eq!(stellar_policy.max_fee, Some(50000));
832 assert_eq!(stellar_policy.timeout_seconds, None);
833 assert_eq!(stellar_policy.min_balance, None);
834 } else {
835 panic!("Expected Stellar policy");
836 }
837 }
838 }
839
840 #[test]
841 fn test_notification_id_deserialization() {
842 let json_with_notification = r#"{
844 "name": "Test Relayer",
845 "notification_id": "notif-123"
846 }"#;
847
848 let request: UpdateRelayerRequestRaw =
849 serde_json::from_str(json_with_notification).unwrap();
850 assert_eq!(request.notification_id, Some("notif-123".to_string()));
851
852 let json_without_notification = r#"{
854 "name": "Test Relayer"
855 }"#;
856
857 let request: UpdateRelayerRequestRaw =
858 serde_json::from_str(json_without_notification).unwrap();
859 assert_eq!(request.notification_id, None);
860
861 let invalid_json = r#"{
863 "name": "Test Relayer",
864 "notification_id": 123
865 }"#;
866
867 let result = serde_json::from_str::<UpdateRelayerRequestRaw>(invalid_json);
868 assert!(result.is_err());
869 }
870
871 #[test]
872 fn test_comprehensive_update_request() {
873 let json_input = r#"{
875 "name": "Updated Relayer",
876 "paused": true,
877 "notification_id": "new-notification-id",
878 "policies": {
879 "min_balance": "5000000000000000000",
880 "gas_limit_estimation": false
881 },
882 "custom_rpc_urls": [
883 {"url": "https://example.com", "weight": 100}
884 ]
885 }"#;
886
887 let request: UpdateRelayerRequestRaw = serde_json::from_str(json_input).unwrap();
888
889 assert_eq!(request.name, Some("Updated Relayer".to_string()));
891 assert_eq!(request.paused, Some(true));
892 assert_eq!(
893 request.notification_id,
894 Some("new-notification-id".to_string())
895 );
896 assert!(request.policies.is_some());
897 assert!(request.custom_rpc_urls.is_some());
898
899 if let Some(policies_json) = &request.policies {
901 assert!(policies_json.get("min_balance").is_some());
903 assert!(policies_json.get("gas_limit_estimation").is_some());
904 } else {
905 panic!("Expected policies");
906 }
907 }
908
909 #[test]
910 fn test_comprehensive_update_request_stellar() {
911 let json_input = r#"{
913 "name": "Updated Stellar Relayer",
914 "paused": false,
915 "notification_id": "stellar-notification",
916 "policies": {
917 "min_balance": 30000000,
918 "max_fee": 250000,
919 "timeout_seconds": 90
920 },
921 "custom_rpc_urls": [
922 {"url": "https://stellar-node.example.com", "weight": 100}
923 ]
924 }"#;
925
926 let request: UpdateRelayerRequestRaw = serde_json::from_str(json_input).unwrap();
927
928 assert_eq!(request.name, Some("Updated Stellar Relayer".to_string()));
930 assert_eq!(request.paused, Some(false));
931 assert_eq!(
932 request.notification_id,
933 Some("stellar-notification".to_string())
934 );
935 assert!(request.policies.is_some());
936 assert!(request.custom_rpc_urls.is_some());
937
938 if let Some(policies_json) = &request.policies {
940 let network_policy =
941 deserialize_policy_for_network_type(policies_json, RelayerNetworkType::Stellar)
942 .unwrap();
943 if let RelayerNetworkPolicy::Stellar(stellar_policy) = network_policy {
944 assert_eq!(stellar_policy.min_balance, Some(30000000));
945 assert_eq!(stellar_policy.max_fee, Some(250000));
946 assert_eq!(stellar_policy.timeout_seconds, Some(90));
947 } else {
948 panic!("Expected Stellar policy");
949 }
950 }
951 }
952
953 #[test]
954 fn test_create_request_network_type_based_policy_deserialization() {
955 let evm_json = r#"{
958 "name": "EVM Relayer",
959 "network": "mainnet",
960 "paused": false,
961 "network_type": "evm",
962 "signer_id": "test-signer",
963 "policies": {
964 "gas_price_cap": 50000000000,
965 "eip1559_pricing": true,
966 "min_balance": "1000000000000000000"
967 }
968 }"#;
969
970 let evm_request: CreateRelayerRequest = serde_json::from_str(evm_json).unwrap();
971 assert_eq!(evm_request.network_type, RelayerNetworkType::Evm);
972
973 if let Some(CreateRelayerPolicyRequest::Evm(evm_policy)) = evm_request.policies {
974 assert_eq!(evm_policy.gas_price_cap, Some(50000000000));
975 assert_eq!(evm_policy.eip1559_pricing, Some(true));
976 assert_eq!(evm_policy.min_balance, Some(1000000000000000000));
977 } else {
978 panic!("Expected EVM policy");
979 }
980
981 let solana_json = r#"{
983 "name": "Solana Relayer",
984 "network": "mainnet",
985 "paused": false,
986 "network_type": "solana",
987 "signer_id": "test-signer",
988 "policies": {
989 "fee_payment_strategy": "relayer",
990 "min_balance": 5000000,
991 "max_signatures": 10
992 }
993 }"#;
994
995 let solana_request: CreateRelayerRequest = serde_json::from_str(solana_json).unwrap();
996 assert_eq!(solana_request.network_type, RelayerNetworkType::Solana);
997
998 if let Some(CreateRelayerPolicyRequest::Solana(solana_policy)) = solana_request.policies {
999 assert_eq!(solana_policy.min_balance, Some(5000000));
1000 assert_eq!(solana_policy.max_signatures, Some(10));
1001 } else {
1002 panic!("Expected Solana policy");
1003 }
1004
1005 let stellar_json = r#"{
1007 "name": "Stellar Relayer",
1008 "network": "mainnet",
1009 "paused": false,
1010 "network_type": "stellar",
1011 "signer_id": "test-signer",
1012 "policies": {
1013 "min_balance": 40000000,
1014 "max_fee": 300000,
1015 "timeout_seconds": 180
1016 }
1017 }"#;
1018
1019 let stellar_request: CreateRelayerRequest = serde_json::from_str(stellar_json).unwrap();
1020 assert_eq!(stellar_request.network_type, RelayerNetworkType::Stellar);
1021
1022 if let Some(CreateRelayerPolicyRequest::Stellar(stellar_policy)) = stellar_request.policies
1023 {
1024 assert_eq!(stellar_policy.min_balance, Some(40000000));
1025 assert_eq!(stellar_policy.max_fee, Some(300000));
1026 assert_eq!(stellar_policy.timeout_seconds, Some(180));
1027 } else {
1028 panic!("Expected Stellar policy");
1029 }
1030
1031 let invalid_json = r#"{
1033 "name": "Invalid Relayer",
1034 "network": "mainnet",
1035 "paused": false,
1036 "network_type": "evm",
1037 "signer_id": "test-signer",
1038 "policies": {
1039 "fee_payment_strategy": "relayer"
1040 }
1041 }"#;
1042
1043 let result = serde_json::from_str::<CreateRelayerRequest>(invalid_json);
1044 assert!(result.is_err());
1045 assert!(result.unwrap_err().to_string().contains("unknown field"));
1046 }
1047
1048 #[test]
1049 fn test_create_request_invalid_stellar_policy_fields() {
1050 let invalid_json = r#"{
1052 "name": "Invalid Stellar Relayer",
1053 "network": "mainnet",
1054 "paused": false,
1055 "network_type": "stellar",
1056 "signer_id": "test-signer",
1057 "policies": {
1058 "gas_price_cap": 100000000000
1059 }
1060 }"#;
1061
1062 let result = serde_json::from_str::<CreateRelayerRequest>(invalid_json);
1063 assert!(result.is_err());
1064 assert!(result.unwrap_err().to_string().contains("unknown field"));
1065 }
1066
1067 #[test]
1068 fn test_create_request_empty_policies() {
1069 let evm_json = r#"{
1071 "name": "EVM Relayer No Policies",
1072 "network": "mainnet",
1073 "paused": false,
1074 "network_type": "evm",
1075 "signer_id": "test-signer"
1076 }"#;
1077
1078 let evm_request: CreateRelayerRequest = serde_json::from_str(evm_json).unwrap();
1079 assert_eq!(evm_request.network_type, RelayerNetworkType::Evm);
1080 assert!(evm_request.policies.is_none());
1081
1082 let stellar_json = r#"{
1083 "name": "Stellar Relayer No Policies",
1084 "network": "mainnet",
1085 "paused": false,
1086 "network_type": "stellar",
1087 "signer_id": "test-signer"
1088 }"#;
1089
1090 let stellar_request: CreateRelayerRequest = serde_json::from_str(stellar_json).unwrap();
1091 assert_eq!(stellar_request.network_type, RelayerNetworkType::Stellar);
1092 assert!(stellar_request.policies.is_none());
1093
1094 let solana_json = r#"{
1095 "name": "Solana Relayer No Policies",
1096 "network": "mainnet",
1097 "paused": false,
1098 "network_type": "solana",
1099 "signer_id": "test-signer"
1100 }"#;
1101
1102 let solana_request: CreateRelayerRequest = serde_json::from_str(solana_json).unwrap();
1103 assert_eq!(solana_request.network_type, RelayerNetworkType::Solana);
1104 assert!(solana_request.policies.is_none());
1105 }
1106
1107 #[test]
1108 fn test_deserialize_policy_utility_function_all_networks() {
1109 let evm_json = serde_json::json!({
1113 "gas_price_cap": "75000000000",
1114 "private_transactions": false,
1115 "min_balance": "2000000000000000000"
1116 });
1117
1118 let evm_policy =
1119 deserialize_policy_for_network_type(&evm_json, RelayerNetworkType::Evm).unwrap();
1120 if let RelayerNetworkPolicy::Evm(policy) = evm_policy {
1121 assert_eq!(policy.gas_price_cap, Some(75000000000));
1122 assert_eq!(policy.private_transactions, Some(false));
1123 assert_eq!(policy.min_balance, Some(2000000000000000000));
1124 } else {
1125 panic!("Expected EVM policy");
1126 }
1127
1128 let solana_json = serde_json::json!({
1130 "fee_payment_strategy": "user",
1131 "max_tx_data_size": 512,
1132 "fee_margin_percentage": 1.5
1133 });
1134
1135 let solana_policy =
1136 deserialize_policy_for_network_type(&solana_json, RelayerNetworkType::Solana).unwrap();
1137 if let RelayerNetworkPolicy::Solana(policy) = solana_policy {
1138 assert_eq!(
1139 policy.fee_payment_strategy,
1140 Some(SolanaFeePaymentStrategy::User)
1141 );
1142 assert_eq!(policy.max_tx_data_size, Some(512));
1143 assert_eq!(policy.fee_margin_percentage, Some(1.5));
1144 } else {
1145 panic!("Expected Solana policy");
1146 }
1147
1148 let stellar_json = serde_json::json!({
1150 "max_fee": 125000,
1151 "timeout_seconds": 240
1152 });
1153
1154 let stellar_policy =
1155 deserialize_policy_for_network_type(&stellar_json, RelayerNetworkType::Stellar)
1156 .unwrap();
1157 if let RelayerNetworkPolicy::Stellar(policy) = stellar_policy {
1158 assert_eq!(policy.max_fee, Some(125000));
1159 assert_eq!(policy.timeout_seconds, Some(240));
1160 assert_eq!(policy.min_balance, None);
1161 } else {
1162 panic!("Expected Stellar policy");
1163 }
1164 }
1165}