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