1use crate::backend::viviswap::{
2 delete_viviswap_detail, get_viviswap_details, get_viviswap_order, get_viviswap_orders, get_viviswap_payment_method,
3 set_viviswap_contract, set_viviswap_detail,
4};
5use crate::core::Sdk;
6use crate::core::viviswap::ViviswapError;
7use crate::error::Result;
8use crate::types::currencies::Currency;
9use crate::types::newtypes::EncryptionPin;
10use crate::types::viviswap::{
11 ViviswapAddressDetail, ViviswapDeposit, ViviswapDepositDetails, ViviswapDetailUpdateStrategy, ViviswapWithdrawal,
12 ViviswapWithdrawalDetails,
13};
14use api_types::api::viviswap::contract::ViviswapApiContractDetails;
15use api_types::api::viviswap::detail::SwapPaymentDetailKey;
16use api_types::api::viviswap::order::{Order, OrderList};
17use etopay_wallet::types::CryptoAmount;
18use log::{debug, info};
19use rust_decimal_macros::dec;
20
21impl Sdk {
22 pub async fn get_iban_for_viviswap(&mut self) -> Result<ViviswapAddressDetail> {
40 info!("Getting IBAN for viviswap");
41 let mut user = self.get_user().await?;
43
44 let Some(mut viviswap_state) = user.viviswap_state else {
46 return Err(crate::Error::Viviswap(ViviswapError::UserStateExisting));
47 };
48
49 let Some(repo) = &mut self.repo else {
50 return Err(crate::Error::UserRepoNotInitialized);
51 };
52
53 if let Some(iban) = viviswap_state.current_iban {
55 Ok(iban)
57 } else {
58 let access_token = self
59 .access_token
60 .as_ref()
61 .ok_or(crate::error::Error::MissingAccessToken)?;
62 let config = self.config.as_ref().ok_or(crate::Error::MissingConfig)?;
63 let details = get_viviswap_details(config, access_token, SwapPaymentDetailKey::Sepa).await?;
64 if let Some(detail) = details.payment_detail.first() {
67 let new_detail = ViviswapAddressDetail {
68 id: detail.id.clone(),
69 address: detail.address.clone(),
70 is_verified: detail.is_verified.unwrap_or(false),
71 };
72 viviswap_state.current_iban = Option::Some(new_detail.clone());
73 user.viviswap_state = Some(viviswap_state.clone());
74 repo.update(&user)?;
75 Ok(new_detail)
76 } else {
77 Err(crate::Error::Viviswap(ViviswapError::Api(
78 "Unable to find IBAN at viviswap".to_string(),
79 )))
80 }
81 }
82 }
83
84 async fn ensure_detail(
101 &self,
102 address: String,
103 payment_method_key: SwapPaymentDetailKey,
104 some_update_strategy: ViviswapDetailUpdateStrategy,
105 ) -> Result<ViviswapAddressDetail> {
106 debug!("Ensuring payment detail for viviswap");
107 let _user = self.get_user().await?;
109 let access_token = self
110 .access_token
111 .as_ref()
112 .ok_or(crate::error::Error::MissingAccessToken)?;
113 let config = self.config.as_ref().ok_or(crate::Error::MissingConfig)?;
114
115 let details = get_viviswap_details(config, access_token, payment_method_key).await?;
117 debug!("{details:#?}");
118 for detail in &details.payment_detail {
120 debug!("{detail:#?}");
121 let add = detail.address.clone();
122 debug!("{add:#?}");
123 debug!("{address:#?}");
124 if detail.address == address {
125 return Ok(ViviswapAddressDetail {
126 id: detail.id.to_string(),
127 address: detail.address.to_string(),
128 is_verified: detail.is_verified.unwrap_or(false),
129 });
130 }
131 }
132
133 let new_detail = set_viviswap_detail(config, access_token, payment_method_key, &address).await?;
135
136 let Some(detail) = new_detail.payment_detail else {
138 return Err(crate::Error::Viviswap(ViviswapError::Api(
139 "no payment details returned".into(),
140 )));
141 };
142
143 if let (ViviswapDetailUpdateStrategy::Replace, Some(old_detail)) =
145 (some_update_strategy, details.payment_detail.first())
146 {
147 delete_viviswap_detail(config, access_token, payment_method_key, old_detail.id.as_str()).await?
148 }
149
150 Ok(ViviswapAddressDetail {
152 id: detail.id,
153 address: detail.address,
154 is_verified: detail.is_verified.unwrap_or(false),
155 })
156 }
157
158 pub async fn update_iban_for_viviswap(
176 &mut self,
177 pin: &EncryptionPin,
178 address: String,
179 ) -> Result<ViviswapAddressDetail> {
180 info!("Updating user IBAN");
181 self.verify_pin(pin).await?;
183
184 if self.repo.is_none() {
186 return Err(crate::Error::UserRepoNotInitialized);
187 }
188
189 let mut user = self.get_user().await?;
191
192 let Some(mut viviswap_state) = user.viviswap_state else {
194 return Err(crate::Error::Viviswap(ViviswapError::MissingUser));
195 };
196
197 if let Some(current_iban) = viviswap_state.current_iban {
199 if current_iban.address == address {
200 return Ok(current_iban.clone());
201 }
202 }
203
204 let new_detail_response = self
206 .ensure_detail(
207 address,
208 SwapPaymentDetailKey::Sepa,
209 ViviswapDetailUpdateStrategy::Replace,
210 )
211 .await?;
212 let new_detail = new_detail_response;
213
214 if let Some(repo) = &mut self.repo {
218 viviswap_state.current_iban = Option::Some(new_detail.clone());
219 user.viviswap_state = Some(viviswap_state.clone());
220 repo.update(&user)?;
221 } else {
222 return Err(crate::Error::UserRepoNotInitialized);
223 };
224
225 Ok(new_detail)
226 }
227
228 pub async fn create_deposit_with_viviswap(&mut self, pin: &EncryptionPin) -> Result<ViviswapDeposit> {
242 info!("Creating deposit for viviswap");
243 let user = self.get_user().await?;
245 let address = self.generate_new_address(pin).await?;
246
247 let Some(viviswap_state) = user.viviswap_state else {
249 return Err(crate::Error::Viviswap(ViviswapError::MissingUser));
250 };
251
252 let Some(iban_detail) = viviswap_state.current_iban else {
254 return Err(crate::Error::Viviswap(ViviswapError::InvalidState));
255 };
256
257 let iban_method_id = self.get_payment_method_id_viviswap(SwapPaymentDetailKey::Sepa).await?;
258 let network = self.active_network.clone().ok_or(crate::Error::MissingNetwork)?;
259 let currency = Currency::try_from(network.display_symbol)?;
260
261 let payment_method_key = currency.to_vivi_payment_method_key();
262
263 let coin_method_id = self.get_payment_method_id_viviswap(payment_method_key).await?;
264
265 let coin_detail = self
266 .ensure_detail(address, payment_method_key, ViviswapDetailUpdateStrategy::Add)
267 .await?;
268 let config = self.config.as_ref().ok_or(crate::Error::MissingConfig)?;
269
270 let access_token = self
271 .access_token
272 .as_ref()
273 .ok_or(crate::error::Error::MissingAccessToken)?;
274 let contract_response = set_viviswap_contract(
275 config,
276 access_token,
277 CryptoAmount::try_from(dec!(25.0))?, iban_method_id,
279 Option::Some(iban_detail.id),
280 coin_method_id,
281 coin_detail.id,
282 )
283 .await?;
284
285 let new_contract = contract_response
286 .contract
287 .ok_or(crate::Error::Viviswap(ViviswapError::Api(String::from(
288 "Error creating the new contract for user.",
289 ))))?;
290 let bank_details = new_contract
291 .details
292 .ok_or(crate::Error::Viviswap(ViviswapError::Api(String::from(
293 "The new contract has invalid state. Deposit details are missing!",
294 ))))?;
295
296 match bank_details {
297 ViviswapApiContractDetails::BankAccount(account_details) => Ok(ViviswapDeposit {
298 contract_id: new_contract.id,
299 deposit_address: coin_detail.address,
300 details: ViviswapDepositDetails {
301 reference: new_contract.reference,
302 beneficiary: account_details.beneficiary,
303 name_of_bank: account_details.name_of_bank,
304 address_of_bank: account_details.address_of_bank,
305 iban: account_details.address,
306 bic: account_details.bic,
307 },
308 }),
309 _ => Err(crate::Error::Viviswap(ViviswapError::Api(String::from(
310 "The new contract has invalid state. Bank deposit details are missing!",
311 )))),
312 }
313 }
314
315 pub async fn create_detail_for_viviswap(&mut self, pin: &EncryptionPin) -> Result<ViviswapAddressDetail> {
327 let network = self.active_network.clone().ok_or(crate::Error::MissingNetwork)?;
328 let currency = Currency::try_from(network.display_symbol)?;
329 let payment_method_key = currency.to_vivi_payment_method_key();
330
331 info!("Creating a payment detail for viviswap for {payment_method_key:?}");
332 let user = self.get_user().await?;
334
335 if user.viviswap_state.is_none() {
337 return Err(crate::Error::Viviswap(ViviswapError::MissingUser));
338 }
339 let address = self.generate_new_address(pin).await?;
340 let new_detail = self
342 .ensure_detail(address, payment_method_key, ViviswapDetailUpdateStrategy::Add)
343 .await?;
344
345 Ok(new_detail)
346 }
347
348 async fn get_payment_method_id_viviswap(&mut self, payment_method_key: SwapPaymentDetailKey) -> Result<String> {
365 let mut user = self.get_user().await?;
366
367 let Some(repo) = &mut self.repo else {
369 return Err(crate::Error::UserRepoNotInitialized);
370 };
371
372 let Some(mut viviswap_state) = user.viviswap_state else {
374 return Err(crate::Error::Viviswap(ViviswapError::MissingUser));
375 };
376
377 let payment_methods = match viviswap_state.payment_methods {
378 Some(details) => details,
379 None => {
380 let access_token = self
381 .access_token
382 .as_ref()
383 .ok_or(crate::error::Error::MissingAccessToken)?;
384 let config = self.config.as_ref().ok_or(crate::Error::MissingConfig)?;
385 let payment_methods = get_viviswap_payment_method(config, access_token).await?;
386 viviswap_state.payment_methods = Some(payment_methods.clone());
387 user.viviswap_state = Some(viviswap_state.clone());
388 repo.update(&user)?;
389 payment_methods
390 }
391 };
392
393 let method_id = payment_methods
394 .methods
395 .iter()
396 .find(|&method| method.key == payment_method_key)
397 .map(|method| method.id.clone())
398 .ok_or_else(|| {
399 crate::Error::Viviswap(ViviswapError::Api(format!(
400 "Payment method not found for key: {payment_method_key:?}"
401 )))
402 })?;
403
404 Ok(method_id)
405 }
406
407 pub async fn create_withdrawal_with_viviswap(
428 &mut self,
429 amount: CryptoAmount,
430 pin: Option<&EncryptionPin>,
431 data: Option<Vec<u8>>,
432 ) -> Result<ViviswapWithdrawal> {
433 info!("Creating withdrawal with viviswap");
434 if let Some(pin) = pin {
436 self.verify_pin(pin).await?;
437 }
438 let user = self.get_user().await?;
439
440 let Some(viviswap_state) = user.viviswap_state else {
442 return Err(crate::Error::Viviswap(ViviswapError::MissingUser));
443 };
444
445 let network = self.active_network.clone().ok_or(crate::Error::MissingNetwork)?;
446 let currency = Currency::try_from(network.display_symbol)?;
447
448 let Some(iban_detail) = viviswap_state.current_iban else {
450 return Err(crate::Error::Viviswap(ViviswapError::InvalidState));
451 };
452
453 let iban_method_id = self.get_payment_method_id_viviswap(SwapPaymentDetailKey::Sepa).await?;
454
455 let coin_method_id = self
456 .get_payment_method_id_viviswap(currency.to_vivi_payment_method_key())
457 .await?;
458
459 let access_token = self
460 .access_token
461 .as_ref()
462 .ok_or(crate::error::Error::MissingAccessToken)?;
463 let config = self.config.as_ref().ok_or(crate::Error::MissingConfig)?;
464
465 let contract_response = set_viviswap_contract(
466 config,
467 access_token,
468 amount,
469 coin_method_id,
470 Option::None,
471 iban_method_id,
472 iban_detail.id.clone(),
473 )
474 .await?;
475
476 let new_contract = contract_response
477 .contract
478 .ok_or(crate::Error::Viviswap(ViviswapError::Api(String::from(
479 "Error creating the new contract for user.",
480 ))))?;
481 let withdrawal_details =
482 new_contract
483 .details
484 .ok_or(crate::Error::Viviswap(ViviswapError::Api(String::from(
485 "The new contract has invalid state. Withdrawal details are missing!",
486 ))))?;
487
488 match withdrawal_details {
489 ViviswapApiContractDetails::Crypto(crypto_details) => {
490 if let Some(pin) = pin {
491 self.send_amount(pin, &crypto_details.deposit_address, amount, data)
492 .await?;
493 }
494 Ok(ViviswapWithdrawal {
495 contract_id: new_contract.id,
496 deposit_address: iban_detail.address.clone(),
497 details: ViviswapWithdrawalDetails {
498 reference: new_contract.reference,
499 wallet_id: crypto_details.wallet_id,
500 crypto_address: crypto_details.deposit_address,
501 },
502 })
503 }
504 _ => Err(crate::Error::Viviswap(ViviswapError::Api(String::from(
505 "The new contract has invalid state. Crypto deposit details are missing!",
506 )))),
507 }
508 }
509
510 pub async fn get_swap_list(&self, start: u32, limit: u32) -> Result<OrderList> {
524 let Some(_user) = &self.active_user else {
525 return Err(crate::Error::UserRepoNotInitialized);
526 };
527 let config = self.config.as_ref().ok_or(crate::Error::MissingConfig)?;
528 info!("get_swap_list request");
529
530 let access_token = self
531 .access_token
532 .as_ref()
533 .ok_or(crate::error::Error::MissingAccessToken)?;
534 let orders = get_viviswap_orders(config, access_token, start, limit).await?;
535 Ok(orders)
536 }
537
538 pub async fn get_swap_details(&self, order_id: String) -> Result<Order> {
549 let Some(_user) = &self.active_user else {
550 return Err(crate::Error::UserRepoNotInitialized);
551 };
552 let config = self.config.as_ref().ok_or(crate::Error::MissingConfig)?;
553 info!("get_swap_details request for order_id: {order_id}");
554 let access_token = self
555 .access_token
556 .as_ref()
557 .ok_or(crate::error::Error::MissingAccessToken)?;
558 match get_viviswap_order(config, access_token, &order_id).await {
559 Ok(order_detail) => Ok(order_detail),
560 Err(_) => Err(crate::Error::Viviswap(ViviswapError::Api(format!(
561 "Swap id:{order_id} not found"
562 )))),
563 }
564 }
565}
566
567#[cfg(test)]
568mod tests {
569 use super::*;
570 use crate::testing_utils::{
571 ADDRESS, AUTH_PROVIDER, ETH_NETWORK_KEY, HEADER_X_APP_NAME, IOTA_NETWORK_KEY, ORDER_ID, PIN, TOKEN, USERNAME,
572 example_api_network, example_api_networks, example_bank_details, example_contract_response,
573 example_crypto_details, example_get_payment_details_response, example_get_user, example_viviswap_oder_response,
574 set_config,
575 };
576 use crate::types::users::KycType;
577 use crate::{
578 core::Sdk,
579 types::users::ActiveUser,
580 user::MockUserRepo,
581 wallet_manager::{MockWalletManager, WalletBorrow},
582 };
583 use api_types::api::networks::ApiNetwork;
584 use api_types::api::{dlt::SetUserAddressRequest, viviswap::order::GetOrdersResponse};
585 use etopay_wallet::MockWalletUser;
586 use mockito::Matcher;
587 use rand::Rng;
588 use rstest::rstest;
589
590 fn get_active_user() -> ActiveUser {
592 ActiveUser {
593 username: USERNAME.into(),
594 wallet_manager: Box::new(MockWalletManager::new()),
595 mnemonic_derivation_options: Default::default(),
596 }
597 }
598
599 #[tokio::test]
600 async fn test_err_get_swap_list_should_consume_only_authenticated_requests() {
601 let (_srv, config, _cleanup) = set_config().await;
603
604 let mut sdk = Sdk::new(config).unwrap();
605 sdk.repo = Some(Box::new(MockUserRepo::new()));
606 sdk.access_token = Some(TOKEN.clone());
607 sdk.active_user = None;
608
609 let result = sdk.get_swap_list(1, 2).await;
611
612 assert!(result.is_err());
613 }
614
615 #[tokio::test]
616 async fn test_err_get_swap_details_should_consume_only_authenticated_requests() {
617 let (_srv, config, _cleanup) = set_config().await;
619
620 let mut sdk = Sdk::new(config).unwrap();
621 sdk.repo = Some(Box::new(MockUserRepo::new()));
622 sdk.access_token = Some(TOKEN.clone());
623 sdk.active_user = None;
624
625 let result = sdk.get_swap_details(String::from(ORDER_ID)).await;
627
628 assert!(result.is_err());
629 }
630
631 #[tokio::test]
632 async fn test_ok_get_swap_details() {
633 let (mut srv, config, _cleanup) = set_config().await;
635 let mut sdk = Sdk::new(config).unwrap();
636 sdk.repo = Some(Box::new(MockUserRepo::new()));
637 sdk.access_token = Some(TOKEN.clone());
638 sdk.active_user = Some(get_active_user());
639
640 let mock_response = example_viviswap_oder_response();
641 let body = serde_json::to_string(&mock_response).unwrap();
642
643 let mock_server = srv
644 .mock("GET", format!("/api/viviswap/orders?id={}", ORDER_ID).as_str())
645 .match_header(HEADER_X_APP_NAME, AUTH_PROVIDER)
646 .match_header("authorization", format!("Bearer {}", TOKEN.as_str()).as_str())
647 .with_status(200)
648 .with_body(&body)
649 .with_header("content-type", "application/json")
650 .with_body(&body)
651 .create();
652
653 let result = sdk.get_swap_details(String::from(ORDER_ID)).await;
655
656 assert_eq!(result.unwrap(), example_viviswap_oder_response());
658 mock_server.assert();
659 }
660
661 #[tokio::test]
662 async fn test_ok_get_swap_list() {
663 let (mut srv, config, _cleanup) = set_config().await;
665 let mut sdk = Sdk::new(config).unwrap();
666 sdk.repo = Some(Box::new(MockUserRepo::new()));
667 sdk.access_token = Some(TOKEN.clone());
668 sdk.active_user = Some(get_active_user());
669
670 let mock_order = example_viviswap_oder_response();
671 let mock_response = GetOrdersResponse {
672 count: 1,
673 start: 2,
674 limit: 3,
675 orders: vec![mock_order],
676 };
677 let body = serde_json::to_string(&mock_response).unwrap();
678
679 let mock_server = srv
680 .mock("GET", "/api/viviswap/orders?start=2&limit=1")
681 .match_header(HEADER_X_APP_NAME, AUTH_PROVIDER)
682 .match_header("authorization", format!("Bearer {}", TOKEN.as_str()).as_str())
683 .with_status(200)
684 .with_body(&body)
685 .with_header("content-type", "application/json")
686 .with_body(&body)
687 .create();
688
689 let result = sdk.get_swap_list(2, 1).await;
691
692 let result = result.unwrap();
694 assert_eq!(result.orders[0].contract_id, mock_response.orders[0].contract_id);
695 assert_eq!(result.orders[0].crypto_fees, mock_response.orders[0].crypto_fees);
696 assert_eq!(
697 result.orders[0].fees_amount_eur,
698 mock_response.orders[0].fees_amount_eur
699 );
700 mock_server.assert();
701 }
702
703 #[tokio::test]
704 async fn test_err_get_swap_list_server_error() {
705 let (mut srv, config, _cleanup) = set_config().await;
707 let mut sdk = Sdk::new(config).unwrap();
708 sdk.repo = Some(Box::new(MockUserRepo::new()));
709 sdk.access_token = Some(TOKEN.clone());
710 sdk.active_user = Some(get_active_user());
711
712 let mock_response = GetOrdersResponse {
713 count: 1,
714 start: 2,
715 limit: 3,
716 orders: vec![],
717 };
718 let body = serde_json::to_string(&mock_response).unwrap();
719
720 let server_error_status = rand::rng().random_range(400..410);
721 let mock_server = srv
722 .mock("GET", "/api/viviswap/orders?start=2&limit=1")
723 .match_header(HEADER_X_APP_NAME, AUTH_PROVIDER)
724 .match_header("authorization", format!("Bearer {}", TOKEN.as_str()).as_str())
725 .with_status(server_error_status)
726 .with_body(&body)
727 .with_header("content-type", "application/json")
728 .with_body(&body)
729 .create();
730
731 let result = sdk.get_swap_list(2, 1).await;
733
734 assert!(result.is_err());
735 mock_server.assert();
736 }
737
738 #[tokio::test]
739 async fn test_err_get_swap_details_server_error() {
740 let (mut srv, config, _cleanup) = set_config().await;
742 let mut sdk = Sdk::new(config).unwrap();
743 sdk.repo = Some(Box::new(MockUserRepo::new()));
744 sdk.access_token = Some(TOKEN.clone());
745 sdk.active_user = Some(get_active_user());
746
747 let server_error_status = rand::rng().random_range(400..410);
748 let mock_server = srv
749 .mock("GET", format!("/api/viviswap/orders?id={}", ORDER_ID).as_str())
750 .match_header(HEADER_X_APP_NAME, AUTH_PROVIDER)
751 .match_header("authorization", format!("Bearer {}", TOKEN.as_str()).as_str())
752 .with_status(server_error_status)
753 .with_header("content-type", "application/json")
754 .create();
755
756 let result = sdk.get_swap_details(String::from(ORDER_ID)).await;
758
759 assert!(result.is_err());
761 mock_server.assert();
762 }
763
764 #[rstest]
765 #[case(
766 example_api_network(IOTA_NETWORK_KEY.to_string()),
767 SwapPaymentDetailKey::Iota,
768 "/api/viviswap/details?payment_method_key=IOTA"
769 )]
770 #[case(
771 example_api_network(ETH_NETWORK_KEY.to_string()),
772 SwapPaymentDetailKey::Eth,
773 "/api/viviswap/details?payment_method_key=ETH"
774 )]
775 #[tokio::test]
776 async fn it_should_create_viviswap_deposit(
777 #[case] network: ApiNetwork, #[case] payment_detail_key: SwapPaymentDetailKey, #[case] payment_method_path: &str, ) {
781 let (mut srv, config, _cleanup) = set_config().await;
783 let mut sdk = Sdk::new(config).unwrap();
784 sdk.access_token = Some(TOKEN.clone());
785
786 sdk.set_networks(example_api_networks());
787 sdk.set_network(network.key.clone()).await.unwrap(); sdk.refresh_access_token(Some(TOKEN.clone())).await.unwrap();
789
790 let mock_user_repo = example_get_user(payment_detail_key, false, 5, KycType::Viviswap);
791 sdk.repo = Some(Box::new(mock_user_repo));
792
793 let mut mock_wallet_manager = MockWalletManager::new();
794 mock_wallet_manager.expect_try_get().returning({
795 move |_, _, _, _, _, _| {
796 let mut mock_wallet = MockWalletUser::new();
797 mock_wallet
798 .expect_get_address()
799 .once()
800 .returning(move || Ok(ADDRESS.to_string()));
801 Ok(WalletBorrow::from(mock_wallet))
802 }
803 });
804 sdk.active_user = Some(ActiveUser {
805 username: USERNAME.into(),
806 wallet_manager: Box::new(mock_wallet_manager),
807 mnemonic_derivation_options: Default::default(),
808 });
809
810 let contract_mock_response = example_contract_response(example_bank_details());
812 let body = serde_json::to_string(&contract_mock_response).unwrap();
813 let create_viviswap_contract = srv
814 .mock("POST", "/api/viviswap/contracts")
815 .match_header(HEADER_X_APP_NAME, AUTH_PROVIDER)
816 .match_header("authorization", format!("Bearer {}", TOKEN.as_str()).as_str())
817 .with_status(200)
818 .with_body(&body)
819 .with_header("content-type", "application/json")
820 .with_body(&body)
821 .create();
822
823 let payment_details_mock_response = example_get_payment_details_response();
825 let body = serde_json::to_string(&payment_details_mock_response).unwrap();
826 let get_payment_details = srv
827 .mock("GET", payment_method_path) .match_header(HEADER_X_APP_NAME, AUTH_PROVIDER)
829 .match_header("authorization", format!("Bearer {}", TOKEN.as_str()).as_str())
830 .with_status(200)
831 .with_body(&body)
832 .with_header("content-type", "application/json")
833 .with_body(&body)
834 .create();
835
836 let mock_request = SetUserAddressRequest {
837 address: ADDRESS.to_string(),
838 };
839 let body = serde_json::to_string(&mock_request).unwrap();
840
841 let put_user_address = srv
842 .mock("PUT", "/api/user/address")
843 .match_header(HEADER_X_APP_NAME, AUTH_PROVIDER)
844 .match_header("authorization", format!("Bearer {}", TOKEN.as_str()).as_str())
845 .match_query(Matcher::Exact(format!("network_key={}", network.key)))
846 .match_body(Matcher::Exact(body))
847 .with_status(201)
848 .expect(1)
849 .with_header("content-type", "application/json")
850 .create();
851
852 let _ = sdk.create_deposit_with_viviswap(&PIN).await.unwrap();
854
855 put_user_address.assert();
857 get_payment_details.assert();
858 create_viviswap_contract.assert();
859 }
860
861 #[tokio::test]
862 async fn it_should_create_withdrawal_deposit_for_iota_and_smr() {
863 let (mut srv, config, _cleanup) = set_config().await;
865
866 let mut sdk = Sdk::new(config).unwrap();
867 sdk.set_networks(example_api_networks());
868 sdk.set_network(IOTA_NETWORK_KEY.to_string()).await.unwrap();
869
870 let mock_user_repo = example_get_user(SwapPaymentDetailKey::Iota, false, 3, KycType::Viviswap);
871 sdk.repo = Some(Box::new(mock_user_repo));
872
873 sdk.access_token = Some(TOKEN.clone());
874 sdk.active_user = Some(get_active_user());
875
876 let contract_mock_response = example_contract_response(example_crypto_details());
878 let body = serde_json::to_string(&contract_mock_response).unwrap();
879 let create_viviswap_contract = srv
880 .mock("POST", "/api/viviswap/contracts")
881 .match_header(HEADER_X_APP_NAME, AUTH_PROVIDER)
882 .match_header("authorization", format!("Bearer {}", TOKEN.as_str()).as_str())
883 .with_status(200)
884 .with_body(&body)
885 .with_header("content-type", "application/json")
886 .with_body(&body)
887 .create();
888
889 let result = sdk
891 .create_withdrawal_with_viviswap(dec!(50.0).try_into().unwrap(), None, Some(Vec::from([8, 16])))
892 .await;
893
894 result.unwrap();
896 create_viviswap_contract.assert();
897 }
898}