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