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