1#![allow(unused_imports)]
15use async_trait::async_trait;
16use derive_builder::Builder;
17use reqwest;
18use rust_decimal::prelude::*;
19use serde::{Deserialize, Serialize};
20use serde_json::{Value, json};
21use std::collections::BTreeMap;
22
23use crate::common::{
24 config::ConfigurationRestApi,
25 models::{ParamBuildError, RestApiResponse},
26 utils::send_request,
27};
28use crate::simple_earn::rest_api::models;
29
30const HAS_TIME_UNIT: bool = false;
31
32#[async_trait]
33pub trait BfusdApi: Send + Sync {
34 async fn get_bfusd_account(
35 &self,
36 params: GetBfusdAccountParams,
37 ) -> anyhow::Result<RestApiResponse<models::GetBfusdAccountResponse>>;
38 async fn get_bfusd_quota_details(
39 &self,
40 params: GetBfusdQuotaDetailsParams,
41 ) -> anyhow::Result<RestApiResponse<models::GetBfusdQuotaDetailsResponse>>;
42 async fn get_bfusd_rate_history(
43 &self,
44 params: GetBfusdRateHistoryParams,
45 ) -> anyhow::Result<RestApiResponse<models::GetBfusdRateHistoryResponse>>;
46 async fn get_bfusd_redemption_history(
47 &self,
48 params: GetBfusdRedemptionHistoryParams,
49 ) -> anyhow::Result<RestApiResponse<models::GetBfusdRedemptionHistoryResponse>>;
50 async fn get_bfusd_rewards_history(
51 &self,
52 params: GetBfusdRewardsHistoryParams,
53 ) -> anyhow::Result<RestApiResponse<models::GetBfusdRewardsHistoryResponse>>;
54 async fn get_bfusd_subscription_history(
55 &self,
56 params: GetBfusdSubscriptionHistoryParams,
57 ) -> anyhow::Result<RestApiResponse<models::GetBfusdSubscriptionHistoryResponse>>;
58 async fn redeem_bfusd(
59 &self,
60 params: RedeemBfusdParams,
61 ) -> anyhow::Result<RestApiResponse<models::RedeemBfusdResponse>>;
62 async fn subscribe_bfusd(
63 &self,
64 params: SubscribeBfusdParams,
65 ) -> anyhow::Result<RestApiResponse<models::SubscribeBfusdResponse>>;
66}
67
68#[derive(Debug, Clone)]
69pub struct BfusdApiClient {
70 configuration: ConfigurationRestApi,
71}
72
73impl BfusdApiClient {
74 pub fn new(configuration: ConfigurationRestApi) -> Self {
75 Self { configuration }
76 }
77}
78
79#[derive(Clone, Debug, Builder, Default)]
84#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
85pub struct GetBfusdAccountParams {
86 #[builder(setter(into), default)]
90 pub recv_window: Option<i64>,
91}
92
93impl GetBfusdAccountParams {
94 #[must_use]
97 pub fn builder() -> GetBfusdAccountParamsBuilder {
98 GetBfusdAccountParamsBuilder::default()
99 }
100}
101#[derive(Clone, Debug, Builder, Default)]
106#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
107pub struct GetBfusdQuotaDetailsParams {
108 #[builder(setter(into), default)]
112 pub recv_window: Option<i64>,
113}
114
115impl GetBfusdQuotaDetailsParams {
116 #[must_use]
119 pub fn builder() -> GetBfusdQuotaDetailsParamsBuilder {
120 GetBfusdQuotaDetailsParamsBuilder::default()
121 }
122}
123#[derive(Clone, Debug, Builder, Default)]
128#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
129pub struct GetBfusdRateHistoryParams {
130 #[builder(setter(into), default)]
135 pub start_time: Option<i64>,
136 #[builder(setter(into), default)]
141 pub end_time: Option<i64>,
142 #[builder(setter(into), default)]
146 pub current: Option<i64>,
147 #[builder(setter(into), default)]
151 pub size: Option<i64>,
152 #[builder(setter(into), default)]
156 pub recv_window: Option<i64>,
157}
158
159impl GetBfusdRateHistoryParams {
160 #[must_use]
163 pub fn builder() -> GetBfusdRateHistoryParamsBuilder {
164 GetBfusdRateHistoryParamsBuilder::default()
165 }
166}
167#[derive(Clone, Debug, Builder, Default)]
172#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
173pub struct GetBfusdRedemptionHistoryParams {
174 #[builder(setter(into), default)]
179 pub start_time: Option<i64>,
180 #[builder(setter(into), default)]
185 pub end_time: Option<i64>,
186 #[builder(setter(into), default)]
190 pub current: Option<i64>,
191 #[builder(setter(into), default)]
195 pub size: Option<i64>,
196 #[builder(setter(into), default)]
200 pub recv_window: Option<i64>,
201}
202
203impl GetBfusdRedemptionHistoryParams {
204 #[must_use]
207 pub fn builder() -> GetBfusdRedemptionHistoryParamsBuilder {
208 GetBfusdRedemptionHistoryParamsBuilder::default()
209 }
210}
211#[derive(Clone, Debug, Builder, Default)]
216#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
217pub struct GetBfusdRewardsHistoryParams {
218 #[builder(setter(into), default)]
223 pub start_time: Option<i64>,
224 #[builder(setter(into), default)]
229 pub end_time: Option<i64>,
230 #[builder(setter(into), default)]
234 pub current: Option<i64>,
235 #[builder(setter(into), default)]
239 pub size: Option<i64>,
240 #[builder(setter(into), default)]
244 pub recv_window: Option<i64>,
245}
246
247impl GetBfusdRewardsHistoryParams {
248 #[must_use]
251 pub fn builder() -> GetBfusdRewardsHistoryParamsBuilder {
252 GetBfusdRewardsHistoryParamsBuilder::default()
253 }
254}
255#[derive(Clone, Debug, Builder, Default)]
260#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
261pub struct GetBfusdSubscriptionHistoryParams {
262 #[builder(setter(into), default)]
266 pub asset: Option<String>,
267 #[builder(setter(into), default)]
272 pub start_time: Option<i64>,
273 #[builder(setter(into), default)]
278 pub end_time: Option<i64>,
279 #[builder(setter(into), default)]
283 pub current: Option<i64>,
284 #[builder(setter(into), default)]
288 pub size: Option<i64>,
289 #[builder(setter(into), default)]
293 pub recv_window: Option<i64>,
294}
295
296impl GetBfusdSubscriptionHistoryParams {
297 #[must_use]
300 pub fn builder() -> GetBfusdSubscriptionHistoryParamsBuilder {
301 GetBfusdSubscriptionHistoryParamsBuilder::default()
302 }
303}
304#[derive(Clone, Debug, Builder)]
309#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
310pub struct RedeemBfusdParams {
311 #[builder(setter(into))]
315 pub amount: rust_decimal::Decimal,
316 #[builder(setter(into))]
320 pub r#type: String,
321 #[builder(setter(into), default)]
325 pub recv_window: Option<i64>,
326}
327
328impl RedeemBfusdParams {
329 #[must_use]
337 pub fn builder(amount: rust_decimal::Decimal, r#type: String) -> RedeemBfusdParamsBuilder {
338 RedeemBfusdParamsBuilder::default()
339 .amount(amount)
340 .r#type(r#type)
341 }
342}
343#[derive(Clone, Debug, Builder)]
348#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
349pub struct SubscribeBfusdParams {
350 #[builder(setter(into))]
354 pub asset: String,
355 #[builder(setter(into))]
359 pub amount: rust_decimal::Decimal,
360 #[builder(setter(into), default)]
364 pub recv_window: Option<i64>,
365}
366
367impl SubscribeBfusdParams {
368 #[must_use]
376 pub fn builder(asset: String, amount: rust_decimal::Decimal) -> SubscribeBfusdParamsBuilder {
377 SubscribeBfusdParamsBuilder::default()
378 .asset(asset)
379 .amount(amount)
380 }
381}
382
383#[async_trait]
384impl BfusdApi for BfusdApiClient {
385 async fn get_bfusd_account(
386 &self,
387 params: GetBfusdAccountParams,
388 ) -> anyhow::Result<RestApiResponse<models::GetBfusdAccountResponse>> {
389 let GetBfusdAccountParams { recv_window } = params;
390
391 let mut query_params = BTreeMap::new();
392 let body_params = BTreeMap::new();
393
394 if let Some(rw) = recv_window {
395 query_params.insert("recvWindow".to_string(), json!(rw));
396 }
397
398 send_request::<models::GetBfusdAccountResponse>(
399 &self.configuration,
400 "/sapi/v1/bfusd/account",
401 reqwest::Method::GET,
402 query_params,
403 body_params,
404 if HAS_TIME_UNIT {
405 self.configuration.time_unit
406 } else {
407 None
408 },
409 true,
410 )
411 .await
412 }
413
414 async fn get_bfusd_quota_details(
415 &self,
416 params: GetBfusdQuotaDetailsParams,
417 ) -> anyhow::Result<RestApiResponse<models::GetBfusdQuotaDetailsResponse>> {
418 let GetBfusdQuotaDetailsParams { recv_window } = params;
419
420 let mut query_params = BTreeMap::new();
421 let body_params = BTreeMap::new();
422
423 if let Some(rw) = recv_window {
424 query_params.insert("recvWindow".to_string(), json!(rw));
425 }
426
427 send_request::<models::GetBfusdQuotaDetailsResponse>(
428 &self.configuration,
429 "/sapi/v1/bfusd/quota",
430 reqwest::Method::GET,
431 query_params,
432 body_params,
433 if HAS_TIME_UNIT {
434 self.configuration.time_unit
435 } else {
436 None
437 },
438 true,
439 )
440 .await
441 }
442
443 async fn get_bfusd_rate_history(
444 &self,
445 params: GetBfusdRateHistoryParams,
446 ) -> anyhow::Result<RestApiResponse<models::GetBfusdRateHistoryResponse>> {
447 let GetBfusdRateHistoryParams {
448 start_time,
449 end_time,
450 current,
451 size,
452 recv_window,
453 } = params;
454
455 let mut query_params = BTreeMap::new();
456 let body_params = BTreeMap::new();
457
458 if let Some(rw) = start_time {
459 query_params.insert("startTime".to_string(), json!(rw));
460 }
461
462 if let Some(rw) = end_time {
463 query_params.insert("endTime".to_string(), json!(rw));
464 }
465
466 if let Some(rw) = current {
467 query_params.insert("current".to_string(), json!(rw));
468 }
469
470 if let Some(rw) = size {
471 query_params.insert("size".to_string(), json!(rw));
472 }
473
474 if let Some(rw) = recv_window {
475 query_params.insert("recvWindow".to_string(), json!(rw));
476 }
477
478 send_request::<models::GetBfusdRateHistoryResponse>(
479 &self.configuration,
480 "/sapi/v1/bfusd/history/rateHistory",
481 reqwest::Method::GET,
482 query_params,
483 body_params,
484 if HAS_TIME_UNIT {
485 self.configuration.time_unit
486 } else {
487 None
488 },
489 true,
490 )
491 .await
492 }
493
494 async fn get_bfusd_redemption_history(
495 &self,
496 params: GetBfusdRedemptionHistoryParams,
497 ) -> anyhow::Result<RestApiResponse<models::GetBfusdRedemptionHistoryResponse>> {
498 let GetBfusdRedemptionHistoryParams {
499 start_time,
500 end_time,
501 current,
502 size,
503 recv_window,
504 } = params;
505
506 let mut query_params = BTreeMap::new();
507 let body_params = BTreeMap::new();
508
509 if let Some(rw) = start_time {
510 query_params.insert("startTime".to_string(), json!(rw));
511 }
512
513 if let Some(rw) = end_time {
514 query_params.insert("endTime".to_string(), json!(rw));
515 }
516
517 if let Some(rw) = current {
518 query_params.insert("current".to_string(), json!(rw));
519 }
520
521 if let Some(rw) = size {
522 query_params.insert("size".to_string(), json!(rw));
523 }
524
525 if let Some(rw) = recv_window {
526 query_params.insert("recvWindow".to_string(), json!(rw));
527 }
528
529 send_request::<models::GetBfusdRedemptionHistoryResponse>(
530 &self.configuration,
531 "/sapi/v1/bfusd/history/redemptionHistory",
532 reqwest::Method::GET,
533 query_params,
534 body_params,
535 if HAS_TIME_UNIT {
536 self.configuration.time_unit
537 } else {
538 None
539 },
540 true,
541 )
542 .await
543 }
544
545 async fn get_bfusd_rewards_history(
546 &self,
547 params: GetBfusdRewardsHistoryParams,
548 ) -> anyhow::Result<RestApiResponse<models::GetBfusdRewardsHistoryResponse>> {
549 let GetBfusdRewardsHistoryParams {
550 start_time,
551 end_time,
552 current,
553 size,
554 recv_window,
555 } = params;
556
557 let mut query_params = BTreeMap::new();
558 let body_params = BTreeMap::new();
559
560 if let Some(rw) = start_time {
561 query_params.insert("startTime".to_string(), json!(rw));
562 }
563
564 if let Some(rw) = end_time {
565 query_params.insert("endTime".to_string(), json!(rw));
566 }
567
568 if let Some(rw) = current {
569 query_params.insert("current".to_string(), json!(rw));
570 }
571
572 if let Some(rw) = size {
573 query_params.insert("size".to_string(), json!(rw));
574 }
575
576 if let Some(rw) = recv_window {
577 query_params.insert("recvWindow".to_string(), json!(rw));
578 }
579
580 send_request::<models::GetBfusdRewardsHistoryResponse>(
581 &self.configuration,
582 "/sapi/v1/bfusd/history/rewardsHistory",
583 reqwest::Method::GET,
584 query_params,
585 body_params,
586 if HAS_TIME_UNIT {
587 self.configuration.time_unit
588 } else {
589 None
590 },
591 true,
592 )
593 .await
594 }
595
596 async fn get_bfusd_subscription_history(
597 &self,
598 params: GetBfusdSubscriptionHistoryParams,
599 ) -> anyhow::Result<RestApiResponse<models::GetBfusdSubscriptionHistoryResponse>> {
600 let GetBfusdSubscriptionHistoryParams {
601 asset,
602 start_time,
603 end_time,
604 current,
605 size,
606 recv_window,
607 } = params;
608
609 let mut query_params = BTreeMap::new();
610 let body_params = BTreeMap::new();
611
612 if let Some(rw) = asset {
613 query_params.insert("asset".to_string(), json!(rw));
614 }
615
616 if let Some(rw) = start_time {
617 query_params.insert("startTime".to_string(), json!(rw));
618 }
619
620 if let Some(rw) = end_time {
621 query_params.insert("endTime".to_string(), json!(rw));
622 }
623
624 if let Some(rw) = current {
625 query_params.insert("current".to_string(), json!(rw));
626 }
627
628 if let Some(rw) = size {
629 query_params.insert("size".to_string(), json!(rw));
630 }
631
632 if let Some(rw) = recv_window {
633 query_params.insert("recvWindow".to_string(), json!(rw));
634 }
635
636 send_request::<models::GetBfusdSubscriptionHistoryResponse>(
637 &self.configuration,
638 "/sapi/v1/bfusd/history/subscriptionHistory",
639 reqwest::Method::GET,
640 query_params,
641 body_params,
642 if HAS_TIME_UNIT {
643 self.configuration.time_unit
644 } else {
645 None
646 },
647 true,
648 )
649 .await
650 }
651
652 async fn redeem_bfusd(
653 &self,
654 params: RedeemBfusdParams,
655 ) -> anyhow::Result<RestApiResponse<models::RedeemBfusdResponse>> {
656 let RedeemBfusdParams {
657 amount,
658 r#type,
659 recv_window,
660 } = params;
661
662 let mut query_params = BTreeMap::new();
663 let body_params = BTreeMap::new();
664
665 query_params.insert("amount".to_string(), json!(amount));
666
667 query_params.insert("type".to_string(), json!(r#type));
668
669 if let Some(rw) = recv_window {
670 query_params.insert("recvWindow".to_string(), json!(rw));
671 }
672
673 send_request::<models::RedeemBfusdResponse>(
674 &self.configuration,
675 "/sapi/v1/bfusd/redeem",
676 reqwest::Method::POST,
677 query_params,
678 body_params,
679 if HAS_TIME_UNIT {
680 self.configuration.time_unit
681 } else {
682 None
683 },
684 true,
685 )
686 .await
687 }
688
689 async fn subscribe_bfusd(
690 &self,
691 params: SubscribeBfusdParams,
692 ) -> anyhow::Result<RestApiResponse<models::SubscribeBfusdResponse>> {
693 let SubscribeBfusdParams {
694 asset,
695 amount,
696 recv_window,
697 } = params;
698
699 let mut query_params = BTreeMap::new();
700 let body_params = BTreeMap::new();
701
702 query_params.insert("asset".to_string(), json!(asset));
703
704 query_params.insert("amount".to_string(), json!(amount));
705
706 if let Some(rw) = recv_window {
707 query_params.insert("recvWindow".to_string(), json!(rw));
708 }
709
710 send_request::<models::SubscribeBfusdResponse>(
711 &self.configuration,
712 "/sapi/v1/bfusd/subscribe",
713 reqwest::Method::POST,
714 query_params,
715 body_params,
716 if HAS_TIME_UNIT {
717 self.configuration.time_unit
718 } else {
719 None
720 },
721 true,
722 )
723 .await
724 }
725}
726
727#[cfg(all(test, feature = "simple_earn"))]
728mod tests {
729 use super::*;
730 use crate::TOKIO_SHARED_RT;
731 use crate::{errors::ConnectorError, models::DataFuture, models::RestApiRateLimit};
732 use async_trait::async_trait;
733 use std::collections::HashMap;
734
735 struct DummyRestApiResponse<T> {
736 inner: Box<dyn FnOnce() -> DataFuture<Result<T, ConnectorError>> + Send + Sync>,
737 status: u16,
738 headers: HashMap<String, String>,
739 rate_limits: Option<Vec<RestApiRateLimit>>,
740 }
741
742 impl<T> From<DummyRestApiResponse<T>> for RestApiResponse<T> {
743 fn from(dummy: DummyRestApiResponse<T>) -> Self {
744 Self {
745 data_fn: dummy.inner,
746 status: dummy.status,
747 headers: dummy.headers,
748 rate_limits: dummy.rate_limits,
749 }
750 }
751 }
752
753 struct MockBfusdApiClient {
754 force_error: bool,
755 }
756
757 #[async_trait]
758 impl BfusdApi for MockBfusdApiClient {
759 async fn get_bfusd_account(
760 &self,
761 _params: GetBfusdAccountParams,
762 ) -> anyhow::Result<RestApiResponse<models::GetBfusdAccountResponse>> {
763 if self.force_error {
764 return Err(ConnectorError::ConnectorClientError {
765 msg: "ResponseError".to_string(),
766 code: None,
767 }
768 .into());
769 }
770
771 let resp_json: Value = serde_json::from_str(
772 r#"{"bfusdAmount":"100","usdtProfit":"11.00","bfusdProfit":"1.81"}"#,
773 )
774 .unwrap();
775 let dummy_response: models::GetBfusdAccountResponse =
776 serde_json::from_value(resp_json.clone())
777 .expect("should parse into models::GetBfusdAccountResponse");
778
779 let dummy = DummyRestApiResponse {
780 inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
781 status: 200,
782 headers: HashMap::new(),
783 rate_limits: None,
784 };
785
786 Ok(dummy.into())
787 }
788
789 async fn get_bfusd_quota_details(
790 &self,
791 _params: GetBfusdQuotaDetailsParams,
792 ) -> anyhow::Result<RestApiResponse<models::GetBfusdQuotaDetailsResponse>> {
793 if self.force_error {
794 return Err(ConnectorError::ConnectorClientError {
795 msg: "ResponseError".to_string(),
796 code: None,
797 }
798 .into());
799 }
800
801 let resp_json: Value = serde_json::from_str(r#"{"subscriptionQuota":{"leftQuota":"1000"},"fastRedemptionQuota":{"leftQuota":"2","minimum":"0.1","fee":"0.001","freeQuota":"100"},"standardRedemptionQuota":{"leftQuota":"2","minimum":"0.1","fee":"0.0005","redeemPeriod":3}}"#).unwrap();
802 let dummy_response: models::GetBfusdQuotaDetailsResponse =
803 serde_json::from_value(resp_json.clone())
804 .expect("should parse into models::GetBfusdQuotaDetailsResponse");
805
806 let dummy = DummyRestApiResponse {
807 inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
808 status: 200,
809 headers: HashMap::new(),
810 rate_limits: None,
811 };
812
813 Ok(dummy.into())
814 }
815
816 async fn get_bfusd_rate_history(
817 &self,
818 _params: GetBfusdRateHistoryParams,
819 ) -> anyhow::Result<RestApiResponse<models::GetBfusdRateHistoryResponse>> {
820 if self.force_error {
821 return Err(ConnectorError::ConnectorClientError {
822 msg: "ResponseError".to_string(),
823 code: None,
824 }
825 .into());
826 }
827
828 let resp_json: Value = serde_json::from_str(
829 r#"{"rows":[{"annualPercentageRate":"0.0418","time":1577233578000}],"total":"1"}"#,
830 )
831 .unwrap();
832 let dummy_response: models::GetBfusdRateHistoryResponse =
833 serde_json::from_value(resp_json.clone())
834 .expect("should parse into models::GetBfusdRateHistoryResponse");
835
836 let dummy = DummyRestApiResponse {
837 inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
838 status: 200,
839 headers: HashMap::new(),
840 rate_limits: None,
841 };
842
843 Ok(dummy.into())
844 }
845
846 async fn get_bfusd_redemption_history(
847 &self,
848 _params: GetBfusdRedemptionHistoryParams,
849 ) -> anyhow::Result<RestApiResponse<models::GetBfusdRedemptionHistoryResponse>> {
850 if self.force_error {
851 return Err(ConnectorError::ConnectorClientError {
852 msg: "ResponseError".to_string(),
853 code: None,
854 }
855 .into());
856 }
857
858 let resp_json: Value = serde_json::from_str(r#"{"rows":[{"time":1575018510000,"asset":"BFUSD","amount":"51","receiveAsset":"USDT","receiveAmount":"50","fee":"1","arrivalTime":1575018510000,"status":"SUCCESS"}],"total":1}"#).unwrap();
859 let dummy_response: models::GetBfusdRedemptionHistoryResponse =
860 serde_json::from_value(resp_json.clone())
861 .expect("should parse into models::GetBfusdRedemptionHistoryResponse");
862
863 let dummy = DummyRestApiResponse {
864 inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
865 status: 200,
866 headers: HashMap::new(),
867 rate_limits: None,
868 };
869
870 Ok(dummy.into())
871 }
872
873 async fn get_bfusd_rewards_history(
874 &self,
875 _params: GetBfusdRewardsHistoryParams,
876 ) -> anyhow::Result<RestApiResponse<models::GetBfusdRewardsHistoryResponse>> {
877 if self.force_error {
878 return Err(ConnectorError::ConnectorClientError {
879 msg: "ResponseError".to_string(),
880 code: None,
881 }
882 .into());
883 }
884
885 let resp_json: Value = serde_json::from_str(r#"{"rows":[{"time":1575018510000,"rewardAsset":"BFUSD","rewardsAmount":"1","BFUSDPosition":"100","annualPercentageRate":"0.0418"}],"total":1}"#).unwrap();
886 let dummy_response: models::GetBfusdRewardsHistoryResponse =
887 serde_json::from_value(resp_json.clone())
888 .expect("should parse into models::GetBfusdRewardsHistoryResponse");
889
890 let dummy = DummyRestApiResponse {
891 inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
892 status: 200,
893 headers: HashMap::new(),
894 rate_limits: None,
895 };
896
897 Ok(dummy.into())
898 }
899
900 async fn get_bfusd_subscription_history(
901 &self,
902 _params: GetBfusdSubscriptionHistoryParams,
903 ) -> anyhow::Result<RestApiResponse<models::GetBfusdSubscriptionHistoryResponse>> {
904 if self.force_error {
905 return Err(ConnectorError::ConnectorClientError {
906 msg: "ResponseError".to_string(),
907 code: None,
908 }
909 .into());
910 }
911
912 let resp_json: Value = serde_json::from_str(r#"{"rows":[{"time":1575018510000,"asset":"USDT","amount":"100","receiveAsset":"BFUSD","receiveAmount":"100","status":"SUCCESS"}],"total":1}"#).unwrap();
913 let dummy_response: models::GetBfusdSubscriptionHistoryResponse =
914 serde_json::from_value(resp_json.clone())
915 .expect("should parse into models::GetBfusdSubscriptionHistoryResponse");
916
917 let dummy = DummyRestApiResponse {
918 inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
919 status: 200,
920 headers: HashMap::new(),
921 rate_limits: None,
922 };
923
924 Ok(dummy.into())
925 }
926
927 async fn redeem_bfusd(
928 &self,
929 _params: RedeemBfusdParams,
930 ) -> anyhow::Result<RestApiResponse<models::RedeemBfusdResponse>> {
931 if self.force_error {
932 return Err(ConnectorError::ConnectorClientError {
933 msg: "ResponseError".to_string(),
934 code: None,
935 }
936 .into());
937 }
938
939 let resp_json: Value = serde_json::from_str(r#"{"success":true,"receiveAmount":"0.23092091","fee":"0.00000012","arrivalTime":1575018510000}"#).unwrap();
940 let dummy_response: models::RedeemBfusdResponse =
941 serde_json::from_value(resp_json.clone())
942 .expect("should parse into models::RedeemBfusdResponse");
943
944 let dummy = DummyRestApiResponse {
945 inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
946 status: 200,
947 headers: HashMap::new(),
948 rate_limits: None,
949 };
950
951 Ok(dummy.into())
952 }
953
954 async fn subscribe_bfusd(
955 &self,
956 _params: SubscribeBfusdParams,
957 ) -> anyhow::Result<RestApiResponse<models::SubscribeBfusdResponse>> {
958 if self.force_error {
959 return Err(ConnectorError::ConnectorClientError {
960 msg: "ResponseError".to_string(),
961 code: None,
962 }
963 .into());
964 }
965
966 let resp_json: Value =
967 serde_json::from_str(r#"{"success":true,"bfusdAmount":"0.22091092"}"#).unwrap();
968 let dummy_response: models::SubscribeBfusdResponse =
969 serde_json::from_value(resp_json.clone())
970 .expect("should parse into models::SubscribeBfusdResponse");
971
972 let dummy = DummyRestApiResponse {
973 inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
974 status: 200,
975 headers: HashMap::new(),
976 rate_limits: None,
977 };
978
979 Ok(dummy.into())
980 }
981 }
982
983 #[test]
984 fn get_bfusd_account_required_params_success() {
985 TOKIO_SHARED_RT.block_on(async {
986 let client = MockBfusdApiClient { force_error: false };
987
988 let params = GetBfusdAccountParams::builder().build().unwrap();
989
990 let resp_json: Value = serde_json::from_str(
991 r#"{"bfusdAmount":"100","usdtProfit":"11.00","bfusdProfit":"1.81"}"#,
992 )
993 .unwrap();
994 let expected_response: models::GetBfusdAccountResponse =
995 serde_json::from_value(resp_json.clone())
996 .expect("should parse into models::GetBfusdAccountResponse");
997
998 let resp = client
999 .get_bfusd_account(params)
1000 .await
1001 .expect("Expected a response");
1002 let data_future = resp.data();
1003 let actual_response = data_future.await.unwrap();
1004 assert_eq!(actual_response, expected_response);
1005 });
1006 }
1007
1008 #[test]
1009 fn get_bfusd_account_optional_params_success() {
1010 TOKIO_SHARED_RT.block_on(async {
1011 let client = MockBfusdApiClient { force_error: false };
1012
1013 let params = GetBfusdAccountParams::builder()
1014 .recv_window(5000)
1015 .build()
1016 .unwrap();
1017
1018 let resp_json: Value = serde_json::from_str(
1019 r#"{"bfusdAmount":"100","usdtProfit":"11.00","bfusdProfit":"1.81"}"#,
1020 )
1021 .unwrap();
1022 let expected_response: models::GetBfusdAccountResponse =
1023 serde_json::from_value(resp_json.clone())
1024 .expect("should parse into models::GetBfusdAccountResponse");
1025
1026 let resp = client
1027 .get_bfusd_account(params)
1028 .await
1029 .expect("Expected a response");
1030 let data_future = resp.data();
1031 let actual_response = data_future.await.unwrap();
1032 assert_eq!(actual_response, expected_response);
1033 });
1034 }
1035
1036 #[test]
1037 fn get_bfusd_account_response_error() {
1038 TOKIO_SHARED_RT.block_on(async {
1039 let client = MockBfusdApiClient { force_error: true };
1040
1041 let params = GetBfusdAccountParams::builder().build().unwrap();
1042
1043 match client.get_bfusd_account(params).await {
1044 Ok(_) => panic!("Expected an error"),
1045 Err(err) => {
1046 assert_eq!(err.to_string(), "Connector client error: ResponseError");
1047 }
1048 }
1049 });
1050 }
1051
1052 #[test]
1053 fn get_bfusd_quota_details_required_params_success() {
1054 TOKIO_SHARED_RT.block_on(async {
1055 let client = MockBfusdApiClient { force_error: false };
1056
1057 let params = GetBfusdQuotaDetailsParams::builder().build().unwrap();
1058
1059 let resp_json: Value = serde_json::from_str(r#"{"subscriptionQuota":{"leftQuota":"1000"},"fastRedemptionQuota":{"leftQuota":"2","minimum":"0.1","fee":"0.001","freeQuota":"100"},"standardRedemptionQuota":{"leftQuota":"2","minimum":"0.1","fee":"0.0005","redeemPeriod":3}}"#).unwrap();
1060 let expected_response : models::GetBfusdQuotaDetailsResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::GetBfusdQuotaDetailsResponse");
1061
1062 let resp = client.get_bfusd_quota_details(params).await.expect("Expected a response");
1063 let data_future = resp.data();
1064 let actual_response = data_future.await.unwrap();
1065 assert_eq!(actual_response, expected_response);
1066 });
1067 }
1068
1069 #[test]
1070 fn get_bfusd_quota_details_optional_params_success() {
1071 TOKIO_SHARED_RT.block_on(async {
1072 let client = MockBfusdApiClient { force_error: false };
1073
1074 let params = GetBfusdQuotaDetailsParams::builder().recv_window(5000).build().unwrap();
1075
1076 let resp_json: Value = serde_json::from_str(r#"{"subscriptionQuota":{"leftQuota":"1000"},"fastRedemptionQuota":{"leftQuota":"2","minimum":"0.1","fee":"0.001","freeQuota":"100"},"standardRedemptionQuota":{"leftQuota":"2","minimum":"0.1","fee":"0.0005","redeemPeriod":3}}"#).unwrap();
1077 let expected_response : models::GetBfusdQuotaDetailsResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::GetBfusdQuotaDetailsResponse");
1078
1079 let resp = client.get_bfusd_quota_details(params).await.expect("Expected a response");
1080 let data_future = resp.data();
1081 let actual_response = data_future.await.unwrap();
1082 assert_eq!(actual_response, expected_response);
1083 });
1084 }
1085
1086 #[test]
1087 fn get_bfusd_quota_details_response_error() {
1088 TOKIO_SHARED_RT.block_on(async {
1089 let client = MockBfusdApiClient { force_error: true };
1090
1091 let params = GetBfusdQuotaDetailsParams::builder().build().unwrap();
1092
1093 match client.get_bfusd_quota_details(params).await {
1094 Ok(_) => panic!("Expected an error"),
1095 Err(err) => {
1096 assert_eq!(err.to_string(), "Connector client error: ResponseError");
1097 }
1098 }
1099 });
1100 }
1101
1102 #[test]
1103 fn get_bfusd_rate_history_required_params_success() {
1104 TOKIO_SHARED_RT.block_on(async {
1105 let client = MockBfusdApiClient { force_error: false };
1106
1107 let params = GetBfusdRateHistoryParams::builder().build().unwrap();
1108
1109 let resp_json: Value = serde_json::from_str(
1110 r#"{"rows":[{"annualPercentageRate":"0.0418","time":1577233578000}],"total":"1"}"#,
1111 )
1112 .unwrap();
1113 let expected_response: models::GetBfusdRateHistoryResponse =
1114 serde_json::from_value(resp_json.clone())
1115 .expect("should parse into models::GetBfusdRateHistoryResponse");
1116
1117 let resp = client
1118 .get_bfusd_rate_history(params)
1119 .await
1120 .expect("Expected a response");
1121 let data_future = resp.data();
1122 let actual_response = data_future.await.unwrap();
1123 assert_eq!(actual_response, expected_response);
1124 });
1125 }
1126
1127 #[test]
1128 fn get_bfusd_rate_history_optional_params_success() {
1129 TOKIO_SHARED_RT.block_on(async {
1130 let client = MockBfusdApiClient { force_error: false };
1131
1132 let params = GetBfusdRateHistoryParams::builder()
1133 .start_time(1623319461670)
1134 .end_time(1641782889000)
1135 .current(1)
1136 .size(10)
1137 .recv_window(5000)
1138 .build()
1139 .unwrap();
1140
1141 let resp_json: Value = serde_json::from_str(
1142 r#"{"rows":[{"annualPercentageRate":"0.0418","time":1577233578000}],"total":"1"}"#,
1143 )
1144 .unwrap();
1145 let expected_response: models::GetBfusdRateHistoryResponse =
1146 serde_json::from_value(resp_json.clone())
1147 .expect("should parse into models::GetBfusdRateHistoryResponse");
1148
1149 let resp = client
1150 .get_bfusd_rate_history(params)
1151 .await
1152 .expect("Expected a response");
1153 let data_future = resp.data();
1154 let actual_response = data_future.await.unwrap();
1155 assert_eq!(actual_response, expected_response);
1156 });
1157 }
1158
1159 #[test]
1160 fn get_bfusd_rate_history_response_error() {
1161 TOKIO_SHARED_RT.block_on(async {
1162 let client = MockBfusdApiClient { force_error: true };
1163
1164 let params = GetBfusdRateHistoryParams::builder().build().unwrap();
1165
1166 match client.get_bfusd_rate_history(params).await {
1167 Ok(_) => panic!("Expected an error"),
1168 Err(err) => {
1169 assert_eq!(err.to_string(), "Connector client error: ResponseError");
1170 }
1171 }
1172 });
1173 }
1174
1175 #[test]
1176 fn get_bfusd_redemption_history_required_params_success() {
1177 TOKIO_SHARED_RT.block_on(async {
1178 let client = MockBfusdApiClient { force_error: false };
1179
1180 let params = GetBfusdRedemptionHistoryParams::builder().build().unwrap();
1181
1182 let resp_json: Value = serde_json::from_str(r#"{"rows":[{"time":1575018510000,"asset":"BFUSD","amount":"51","receiveAsset":"USDT","receiveAmount":"50","fee":"1","arrivalTime":1575018510000,"status":"SUCCESS"}],"total":1}"#).unwrap();
1183 let expected_response : models::GetBfusdRedemptionHistoryResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::GetBfusdRedemptionHistoryResponse");
1184
1185 let resp = client.get_bfusd_redemption_history(params).await.expect("Expected a response");
1186 let data_future = resp.data();
1187 let actual_response = data_future.await.unwrap();
1188 assert_eq!(actual_response, expected_response);
1189 });
1190 }
1191
1192 #[test]
1193 fn get_bfusd_redemption_history_optional_params_success() {
1194 TOKIO_SHARED_RT.block_on(async {
1195 let client = MockBfusdApiClient { force_error: false };
1196
1197 let params = GetBfusdRedemptionHistoryParams::builder().start_time(1623319461670).end_time(1641782889000).current(1).size(10).recv_window(5000).build().unwrap();
1198
1199 let resp_json: Value = serde_json::from_str(r#"{"rows":[{"time":1575018510000,"asset":"BFUSD","amount":"51","receiveAsset":"USDT","receiveAmount":"50","fee":"1","arrivalTime":1575018510000,"status":"SUCCESS"}],"total":1}"#).unwrap();
1200 let expected_response : models::GetBfusdRedemptionHistoryResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::GetBfusdRedemptionHistoryResponse");
1201
1202 let resp = client.get_bfusd_redemption_history(params).await.expect("Expected a response");
1203 let data_future = resp.data();
1204 let actual_response = data_future.await.unwrap();
1205 assert_eq!(actual_response, expected_response);
1206 });
1207 }
1208
1209 #[test]
1210 fn get_bfusd_redemption_history_response_error() {
1211 TOKIO_SHARED_RT.block_on(async {
1212 let client = MockBfusdApiClient { force_error: true };
1213
1214 let params = GetBfusdRedemptionHistoryParams::builder().build().unwrap();
1215
1216 match client.get_bfusd_redemption_history(params).await {
1217 Ok(_) => panic!("Expected an error"),
1218 Err(err) => {
1219 assert_eq!(err.to_string(), "Connector client error: ResponseError");
1220 }
1221 }
1222 });
1223 }
1224
1225 #[test]
1226 fn get_bfusd_rewards_history_required_params_success() {
1227 TOKIO_SHARED_RT.block_on(async {
1228 let client = MockBfusdApiClient { force_error: false };
1229
1230 let params = GetBfusdRewardsHistoryParams::builder().build().unwrap();
1231
1232 let resp_json: Value = serde_json::from_str(r#"{"rows":[{"time":1575018510000,"rewardAsset":"BFUSD","rewardsAmount":"1","BFUSDPosition":"100","annualPercentageRate":"0.0418"}],"total":1}"#).unwrap();
1233 let expected_response : models::GetBfusdRewardsHistoryResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::GetBfusdRewardsHistoryResponse");
1234
1235 let resp = client.get_bfusd_rewards_history(params).await.expect("Expected a response");
1236 let data_future = resp.data();
1237 let actual_response = data_future.await.unwrap();
1238 assert_eq!(actual_response, expected_response);
1239 });
1240 }
1241
1242 #[test]
1243 fn get_bfusd_rewards_history_optional_params_success() {
1244 TOKIO_SHARED_RT.block_on(async {
1245 let client = MockBfusdApiClient { force_error: false };
1246
1247 let params = GetBfusdRewardsHistoryParams::builder().start_time(1623319461670).end_time(1641782889000).current(1).size(10).recv_window(5000).build().unwrap();
1248
1249 let resp_json: Value = serde_json::from_str(r#"{"rows":[{"time":1575018510000,"rewardAsset":"BFUSD","rewardsAmount":"1","BFUSDPosition":"100","annualPercentageRate":"0.0418"}],"total":1}"#).unwrap();
1250 let expected_response : models::GetBfusdRewardsHistoryResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::GetBfusdRewardsHistoryResponse");
1251
1252 let resp = client.get_bfusd_rewards_history(params).await.expect("Expected a response");
1253 let data_future = resp.data();
1254 let actual_response = data_future.await.unwrap();
1255 assert_eq!(actual_response, expected_response);
1256 });
1257 }
1258
1259 #[test]
1260 fn get_bfusd_rewards_history_response_error() {
1261 TOKIO_SHARED_RT.block_on(async {
1262 let client = MockBfusdApiClient { force_error: true };
1263
1264 let params = GetBfusdRewardsHistoryParams::builder().build().unwrap();
1265
1266 match client.get_bfusd_rewards_history(params).await {
1267 Ok(_) => panic!("Expected an error"),
1268 Err(err) => {
1269 assert_eq!(err.to_string(), "Connector client error: ResponseError");
1270 }
1271 }
1272 });
1273 }
1274
1275 #[test]
1276 fn get_bfusd_subscription_history_required_params_success() {
1277 TOKIO_SHARED_RT.block_on(async {
1278 let client = MockBfusdApiClient { force_error: false };
1279
1280 let params = GetBfusdSubscriptionHistoryParams::builder().build().unwrap();
1281
1282 let resp_json: Value = serde_json::from_str(r#"{"rows":[{"time":1575018510000,"asset":"USDT","amount":"100","receiveAsset":"BFUSD","receiveAmount":"100","status":"SUCCESS"}],"total":1}"#).unwrap();
1283 let expected_response : models::GetBfusdSubscriptionHistoryResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::GetBfusdSubscriptionHistoryResponse");
1284
1285 let resp = client.get_bfusd_subscription_history(params).await.expect("Expected a response");
1286 let data_future = resp.data();
1287 let actual_response = data_future.await.unwrap();
1288 assert_eq!(actual_response, expected_response);
1289 });
1290 }
1291
1292 #[test]
1293 fn get_bfusd_subscription_history_optional_params_success() {
1294 TOKIO_SHARED_RT.block_on(async {
1295 let client = MockBfusdApiClient { force_error: false };
1296
1297 let params = GetBfusdSubscriptionHistoryParams::builder().asset("asset_example".to_string()).start_time(1623319461670).end_time(1641782889000).current(1).size(10).recv_window(5000).build().unwrap();
1298
1299 let resp_json: Value = serde_json::from_str(r#"{"rows":[{"time":1575018510000,"asset":"USDT","amount":"100","receiveAsset":"BFUSD","receiveAmount":"100","status":"SUCCESS"}],"total":1}"#).unwrap();
1300 let expected_response : models::GetBfusdSubscriptionHistoryResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::GetBfusdSubscriptionHistoryResponse");
1301
1302 let resp = client.get_bfusd_subscription_history(params).await.expect("Expected a response");
1303 let data_future = resp.data();
1304 let actual_response = data_future.await.unwrap();
1305 assert_eq!(actual_response, expected_response);
1306 });
1307 }
1308
1309 #[test]
1310 fn get_bfusd_subscription_history_response_error() {
1311 TOKIO_SHARED_RT.block_on(async {
1312 let client = MockBfusdApiClient { force_error: true };
1313
1314 let params = GetBfusdSubscriptionHistoryParams::builder()
1315 .build()
1316 .unwrap();
1317
1318 match client.get_bfusd_subscription_history(params).await {
1319 Ok(_) => panic!("Expected an error"),
1320 Err(err) => {
1321 assert_eq!(err.to_string(), "Connector client error: ResponseError");
1322 }
1323 }
1324 });
1325 }
1326
1327 #[test]
1328 fn redeem_bfusd_required_params_success() {
1329 TOKIO_SHARED_RT.block_on(async {
1330 let client = MockBfusdApiClient { force_error: false };
1331
1332 let params = RedeemBfusdParams::builder(dec!(1.0),"s".to_string(),).build().unwrap();
1333
1334 let resp_json: Value = serde_json::from_str(r#"{"success":true,"receiveAmount":"0.23092091","fee":"0.00000012","arrivalTime":1575018510000}"#).unwrap();
1335 let expected_response : models::RedeemBfusdResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::RedeemBfusdResponse");
1336
1337 let resp = client.redeem_bfusd(params).await.expect("Expected a response");
1338 let data_future = resp.data();
1339 let actual_response = data_future.await.unwrap();
1340 assert_eq!(actual_response, expected_response);
1341 });
1342 }
1343
1344 #[test]
1345 fn redeem_bfusd_optional_params_success() {
1346 TOKIO_SHARED_RT.block_on(async {
1347 let client = MockBfusdApiClient { force_error: false };
1348
1349 let params = RedeemBfusdParams::builder(dec!(1.0),"s".to_string(),).recv_window(5000).build().unwrap();
1350
1351 let resp_json: Value = serde_json::from_str(r#"{"success":true,"receiveAmount":"0.23092091","fee":"0.00000012","arrivalTime":1575018510000}"#).unwrap();
1352 let expected_response : models::RedeemBfusdResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::RedeemBfusdResponse");
1353
1354 let resp = client.redeem_bfusd(params).await.expect("Expected a response");
1355 let data_future = resp.data();
1356 let actual_response = data_future.await.unwrap();
1357 assert_eq!(actual_response, expected_response);
1358 });
1359 }
1360
1361 #[test]
1362 fn redeem_bfusd_response_error() {
1363 TOKIO_SHARED_RT.block_on(async {
1364 let client = MockBfusdApiClient { force_error: true };
1365
1366 let params = RedeemBfusdParams::builder(dec!(1.0), "s".to_string())
1367 .build()
1368 .unwrap();
1369
1370 match client.redeem_bfusd(params).await {
1371 Ok(_) => panic!("Expected an error"),
1372 Err(err) => {
1373 assert_eq!(err.to_string(), "Connector client error: ResponseError");
1374 }
1375 }
1376 });
1377 }
1378
1379 #[test]
1380 fn subscribe_bfusd_required_params_success() {
1381 TOKIO_SHARED_RT.block_on(async {
1382 let client = MockBfusdApiClient { force_error: false };
1383
1384 let params = SubscribeBfusdParams::builder("asset_example".to_string(), dec!(1.0))
1385 .build()
1386 .unwrap();
1387
1388 let resp_json: Value =
1389 serde_json::from_str(r#"{"success":true,"bfusdAmount":"0.22091092"}"#).unwrap();
1390 let expected_response: models::SubscribeBfusdResponse =
1391 serde_json::from_value(resp_json.clone())
1392 .expect("should parse into models::SubscribeBfusdResponse");
1393
1394 let resp = client
1395 .subscribe_bfusd(params)
1396 .await
1397 .expect("Expected a response");
1398 let data_future = resp.data();
1399 let actual_response = data_future.await.unwrap();
1400 assert_eq!(actual_response, expected_response);
1401 });
1402 }
1403
1404 #[test]
1405 fn subscribe_bfusd_optional_params_success() {
1406 TOKIO_SHARED_RT.block_on(async {
1407 let client = MockBfusdApiClient { force_error: false };
1408
1409 let params = SubscribeBfusdParams::builder("asset_example".to_string(), dec!(1.0))
1410 .recv_window(5000)
1411 .build()
1412 .unwrap();
1413
1414 let resp_json: Value =
1415 serde_json::from_str(r#"{"success":true,"bfusdAmount":"0.22091092"}"#).unwrap();
1416 let expected_response: models::SubscribeBfusdResponse =
1417 serde_json::from_value(resp_json.clone())
1418 .expect("should parse into models::SubscribeBfusdResponse");
1419
1420 let resp = client
1421 .subscribe_bfusd(params)
1422 .await
1423 .expect("Expected a response");
1424 let data_future = resp.data();
1425 let actual_response = data_future.await.unwrap();
1426 assert_eq!(actual_response, expected_response);
1427 });
1428 }
1429
1430 #[test]
1431 fn subscribe_bfusd_response_error() {
1432 TOKIO_SHARED_RT.block_on(async {
1433 let client = MockBfusdApiClient { force_error: true };
1434
1435 let params = SubscribeBfusdParams::builder("asset_example".to_string(), dec!(1.0))
1436 .build()
1437 .unwrap();
1438
1439 match client.subscribe_bfusd(params).await {
1440 Ok(_) => panic!("Expected an error"),
1441 Err(err) => {
1442 assert_eq!(err.to_string(), "Connector client error: ResponseError");
1443 }
1444 }
1445 });
1446 }
1447}