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