1use super::Sdk;
6use crate::{
7 backend::dlt::put_user_address,
8 error::Result,
9 tx_version::VersionedWalletTransaction,
10 types::newtypes::{EncryptionPin, EncryptionSalt, PlainPassword},
11 wallet::error::{ErrorKind, WalletError},
12};
13use etopay_wallet::{
14 MnemonicDerivationOption,
15 types::{CryptoAmount, WalletTransaction, WalletTxInfoList, WalletTxStatus},
16};
17
18use log::{debug, info, warn};
19
20impl Sdk {
21 pub async fn create_wallet_from_new_mnemonic(&mut self, pin: &EncryptionPin) -> Result<String> {
37 info!("Creating a new wallet from random mnemonic");
38
39 let Some(repo) = &mut self.repo else {
40 return Err(crate::Error::UserRepoNotInitialized);
41 };
42
43 let Some(active_user) = &mut self.active_user else {
44 return Err(crate::Error::UserNotInitialized);
45 };
46
47 let config = self.config.as_ref().ok_or(crate::Error::MissingConfig)?;
48
49 let mnemonic = active_user
50 .wallet_manager
51 .create_wallet_from_new_mnemonic(config, &self.access_token, repo, pin)
52 .await?;
53 Ok(mnemonic)
54 }
55
56 pub async fn create_wallet_from_existing_mnemonic(&mut self, pin: &EncryptionPin, mnemonic: &str) -> Result<()> {
69 info!("Creating a new wallet from existing mnemonic");
70
71 let Some(repo) = &mut self.repo else {
72 return Err(crate::Error::UserRepoNotInitialized);
73 };
74
75 let Some(active_user) = &mut self.active_user else {
76 return Err(crate::Error::UserNotInitialized);
77 };
78
79 let config = self.config.as_ref().ok_or(crate::Error::MissingConfig)?;
80
81 active_user
82 .wallet_manager
83 .create_wallet_from_existing_mnemonic(config, &self.access_token, repo, pin, mnemonic)
84 .await?;
85 Ok(())
86 }
87
88 pub async fn create_wallet_from_backup(
102 &mut self,
103 pin: &EncryptionPin,
104 backup: &[u8],
105 backup_password: &PlainPassword,
106 ) -> Result<()> {
107 info!("Creating a new wallet from backup");
108
109 let Some(repo) = &mut self.repo else {
110 return Err(crate::Error::UserRepoNotInitialized);
111 };
112
113 let Some(active_user) = &mut self.active_user else {
114 return Err(crate::Error::UserNotInitialized);
115 };
116
117 let config = self.config.as_ref().ok_or(crate::Error::MissingConfig)?;
118
119 active_user
120 .wallet_manager
121 .create_wallet_from_backup(config, &self.access_token, repo, pin, backup, backup_password)
122 .await?;
123 Ok(())
124 }
125
126 pub async fn create_wallet_backup(
143 &mut self,
144 pin: &EncryptionPin,
145 backup_password: &PlainPassword,
146 ) -> Result<Vec<u8>> {
147 info!("Creating wallet backup");
148
149 let Some(repo) = &mut self.repo else {
150 return Err(crate::Error::UserRepoNotInitialized);
151 };
152
153 let Some(active_user) = &mut self.active_user else {
154 return Err(crate::Error::UserNotInitialized);
155 };
156
157 let config = self.config.as_ref().ok_or(crate::Error::MissingConfig)?;
158
159 let backup = active_user
160 .wallet_manager
161 .create_wallet_backup(config, &self.access_token, repo, pin, backup_password)
162 .await?;
163 Ok(backup)
164 }
165
166 pub async fn verify_mnemonic(&mut self, pin: &EncryptionPin, mnemonic: &str) -> Result<bool> {
184 info!("Verifying mnemonic");
185
186 let Some(repo) = &mut self.repo else {
187 return Err(crate::Error::UserRepoNotInitialized);
188 };
189
190 let Some(active_user) = &mut self.active_user else {
191 return Err(crate::Error::UserNotInitialized);
192 };
193
194 let config = self.config.as_ref().ok_or(crate::Error::MissingConfig)?;
195
196 let is_verified = active_user
197 .wallet_manager
198 .check_mnemonic(config, &self.access_token, repo, pin, mnemonic)
199 .await?;
200 Ok(is_verified)
201 }
202
203 pub async fn delete_wallet(&mut self, pin: &EncryptionPin) -> Result<()> {
217 warn!("Deleting wallet for user. Potential loss of funds if mnemonic/wallet is not backed up!");
218
219 self.verify_pin(pin).await?;
220
221 let Some(repo) = &mut self.repo else {
222 return Err(crate::Error::UserRepoNotInitialized);
223 };
224 let Some(active_user) = &mut self.active_user else {
225 return Err(crate::Error::UserNotInitialized);
226 };
227
228 let config = self.config.as_ref().ok_or(crate::Error::MissingConfig)?;
229
230 active_user
231 .wallet_manager
232 .delete_wallet(config, &self.access_token, repo)
233 .await?;
234
235 Ok(())
236 }
237
238 pub async fn verify_pin(&self, pin: &EncryptionPin) -> Result<()> {
257 info!("Verifying wallet pin");
258 let Some(repo) = &self.repo else {
259 return Err(crate::Error::UserRepoNotInitialized);
260 };
261 let Some(active_user) = &self.active_user else {
262 return Err(crate::Error::UserNotInitialized);
263 };
264
265 let username = &active_user.username;
266 let user = repo.get(username)?;
267
268 let Some(encrypted_password) = user.encrypted_password else {
270 return Err(WalletError::WalletNotInitialized(ErrorKind::MissingPassword))?;
271 };
272
273 if encrypted_password.decrypt(pin, &user.salt).is_err() {
275 return Err(WalletError::WrongPinOrPassword)?;
276 }
277 Ok(())
278 }
279
280 pub async fn change_pin(&mut self, old_pin: &EncryptionPin, new_pin: &EncryptionPin) -> Result<()> {
300 info!("Resetting pin with password");
301 let Some(repo) = &mut self.repo else {
302 return Err(crate::Error::UserRepoNotInitialized);
303 };
304 let Some(active_user) = &mut self.active_user else {
305 return Err(crate::Error::UserNotInitialized);
306 };
307
308 let username = &active_user.username;
309 let mut user = repo.get(username)?;
310
311 let Some(encrypted_password) = user.encrypted_password else {
312 return Err(WalletError::WalletNotInitialized(ErrorKind::MissingPassword))?;
313 };
314
315 let password = encrypted_password.decrypt(old_pin, &user.salt)?;
317
318 let salt = EncryptionSalt::generate();
320 let encrypted_password = password.encrypt(new_pin, &salt)?;
321
322 user.salt = salt;
324 user.encrypted_password = Some(encrypted_password);
325 repo.update(&user)?;
326
327 Ok(())
328 }
329
330 pub async fn set_wallet_password(&mut self, pin: &EncryptionPin, new_password: &PlainPassword) -> Result<()> {
346 info!("Setting password");
347
348 let Some(repo) = &mut self.repo else {
349 return Err(crate::Error::UserRepoNotInitialized);
350 };
351 let Some(active_user) = &mut self.active_user else {
352 return Err(crate::Error::UserNotInitialized);
353 };
354
355 let mut user = repo.get(&active_user.username)?;
356
357 if let Some(encrypted_password) = user.encrypted_password {
359 info!("Password exists, changing password");
360
361 if encrypted_password.decrypt(pin, &user.salt).is_err() {
363 return Err(WalletError::WrongPinOrPassword)?;
364 }
365
366 let config = self.config.as_ref().ok_or(crate::Error::MissingConfig)?;
367
368 active_user
369 .wallet_manager
370 .change_wallet_password(config, &self.access_token, repo, pin, new_password)
371 .await?;
372 } else {
373 let salt = EncryptionSalt::generate();
375 let encrypted_password = new_password.encrypt(pin, &salt)?;
376
377 user.salt = salt;
379 user.encrypted_password = Some(encrypted_password);
380 repo.update(&user)?;
381 }
382
383 Ok(())
384 }
385
386 pub async fn is_wallet_password_set(&self) -> Result<bool> {
399 info!("Checking if password is set");
400
401 let Some(repo) = &self.repo else {
402 return Err(crate::Error::UserRepoNotInitialized);
403 };
404 let Some(active_user) = &self.active_user else {
405 return Err(crate::Error::UserNotInitialized);
406 };
407
408 let user = repo.get(&active_user.username)?;
409
410 Ok(user.encrypted_password.is_some())
411 }
412
413 pub async fn generate_new_address(&mut self, pin: &EncryptionPin) -> Result<String> {
425 info!("Generating new wallet address");
426 self.verify_pin(pin).await?;
427 let Some(repo) = &mut self.repo else {
428 return Err(crate::Error::UserRepoNotInitialized);
429 };
430 let Some(active_user) = &mut self.active_user else {
431 return Err(crate::Error::UserNotInitialized);
432 };
433 let network = self.active_network.as_ref().ok_or(crate::Error::MissingNetwork)?;
434 let config = self.config.as_mut().ok_or(crate::Error::MissingConfig)?;
435 let wallet = active_user
436 .wallet_manager
437 .try_get(
438 config,
439 &self.access_token,
440 repo,
441 network,
442 pin,
443 &active_user.mnemonic_derivation_options,
444 )
445 .await?;
446
447 let address = wallet.get_address().await?;
448
449 if let Some(access_token) = self.access_token.as_ref() {
451 if network.can_do_purchases {
452 put_user_address(config, access_token, &network.key, &address).await?;
453 }
454 }
455 debug!("Generated address: {address}");
456 Ok(address)
457 }
458
459 pub async fn get_balance(&mut self, pin: &EncryptionPin) -> Result<CryptoAmount> {
472 info!("Fetching balance");
473 self.verify_pin(pin).await?;
474 let wallet = self.try_get_active_user_wallet(pin).await?;
475 let balance = wallet.get_balance().await?;
476 debug!("Balance: {balance:?}");
477 Ok(balance)
478 }
479
480 pub async fn get_wallet_tx_list(
498 &mut self,
499 pin: &EncryptionPin,
500 start: usize,
501 limit: usize,
502 ) -> Result<WalletTxInfoList> {
503 info!("Wallet getting list of transactions");
504 self.verify_pin(pin).await?;
505
506 let Some(repo) = &mut self.repo else {
507 return Err(crate::Error::UserRepoNotInitialized);
508 };
509 let Some(active_user) = &mut self.active_user else {
510 return Err(crate::Error::UserNotInitialized);
511 };
512 let network = self.active_network.as_ref().ok_or(crate::Error::MissingNetwork)?;
513 let config = self.config.as_mut().ok_or(crate::Error::MissingConfig)?;
514 let wallet = active_user
515 .wallet_manager
516 .try_get(
517 config,
518 &self.access_token,
519 repo,
520 network,
521 pin,
522 &active_user.mnemonic_derivation_options,
523 )
524 .await?;
525
526 let user = repo.get(active_user.username.as_str())?;
527
528 let mut wallet_transactions = user.wallet_transactions_versioned;
534
535 match wallet.get_wallet_tx_list(start, limit).await {
537 Ok(transaction_hashes) => {
538 log::debug!("Digests: {:#?}", transaction_hashes);
540 for hash in transaction_hashes {
541 if wallet_transactions
544 .iter()
545 .any(|t| t.transaction_hash() == hash && t.network_key() == network.key)
546 {
547 continue;
548 }
549
550 log::debug!("Getting details for new transaction with hash {hash}");
551
552 match wallet.get_wallet_tx(&hash).await {
554 Err(e) => log::warn!("Could not get transaction details for {hash}: {e}"),
555 Ok(details) => wallet_transactions.push(VersionedWalletTransaction::from(details)),
556 }
557 }
558 }
559 Err(etopay_wallet::WalletError::WalletFeatureNotImplemented) => {}
561 Err(e) => return Err(e.into()),
562 };
563
564 wallet_transactions.sort_by_key(|b| std::cmp::Reverse(b.date()));
565 let mut wallet_tx_list = Vec::new();
566
567 for t in wallet_transactions
568 .iter_mut()
569 .filter(|tx| tx.network_key() == network.key)
570 .skip(start)
571 .take(limit)
572 {
573 if let VersionedWalletTransaction::V1(v1) = t {
575 if let Ok(details) = wallet.get_wallet_tx(&v1.transaction_hash).await {
576 *t = VersionedWalletTransaction::from(details);
577 wallet_tx_list.push(WalletTransaction::from(t.clone()));
578 continue;
579 }
580 }
581
582 if t.status() == WalletTxStatus::Pending {
584 if let Ok(details) = wallet.get_wallet_tx(t.transaction_hash()).await {
585 *t = VersionedWalletTransaction::V2(details);
586 wallet_tx_list.push(WalletTransaction::from(t.clone()));
587 continue;
588 }
589 }
590
591 wallet_tx_list.push(WalletTransaction::from(t.clone()));
592 }
593
594 let _ = repo.set_wallet_transactions(&user.username, wallet_transactions);
596
597 Ok(WalletTxInfoList {
598 transactions: wallet_tx_list,
599 })
600 }
601
602 pub async fn get_wallet_tx(&mut self, pin: &EncryptionPin, tx_id: &str) -> Result<WalletTransaction> {
619 info!("Wallet getting details of particular transactions");
620 self.verify_pin(pin).await?;
621 let wallet = self.try_get_active_user_wallet(pin).await?;
622 let wallet_tx = wallet.get_wallet_tx(tx_id).await?;
623 Ok(wallet_tx)
624 }
625
626 pub async fn set_wallet_derivation_options(&mut self, account: u32, index: u32) -> Result<()> {
637 let options = MnemonicDerivationOption { account, index };
638
639 info!("Setting wallet mnemonic derivation options: {options:?}");
640
641 let Some(active_user) = &mut self.active_user else {
642 return Err(crate::Error::UserNotInitialized);
643 };
644
645 active_user.mnemonic_derivation_options = options;
646
647 Ok(())
648 }
649}
650
651#[cfg(test)]
652mod tests {
653 use super::*;
654 use crate::core::core_testing_utils::handle_error_test_cases;
655 use crate::testing_utils::{
656 ADDRESS, AUTH_PROVIDER, ENCRYPTED_WALLET_PASSWORD, ETH_NETWORK_KEY, HEADER_X_APP_NAME, IOTA_NETWORK_KEY,
657 MNEMONIC, PIN, SALT, TOKEN, TX_INDEX, USERNAME, WALLET_PASSWORD, example_api_networks, example_get_user,
658 example_versioned_wallet_transaction, set_config,
659 };
660 use crate::types::users::UserEntity;
661 use crate::{
662 core::Sdk,
663 types::users::KycType,
664 user::MockUserRepo,
665 wallet_manager::{MockWalletManager, WalletBorrow},
666 };
667 use api_types::api::dlt::SetUserAddressRequest;
668 use api_types::api::viviswap::detail::SwapPaymentDetailKey;
669 use chrono::{DateTime, TimeZone, Utc};
670 use etopay_wallet::MockWalletUser;
671 use etopay_wallet::types::{WalletTransaction, WalletTxStatus};
672 use mockall::predicate::eq;
673 use mockito::Matcher;
674 use rstest::rstest;
675 use rust_decimal_macros::dec;
676 use std::sync::LazyLock;
677
678 const BACKUP: &[u8] = &[42, 77, 15, 203, 89, 123, 34, 56, 178, 90, 210, 33, 47, 192, 1, 17];
679
680 #[rstest]
681 #[case::success(Ok(MNEMONIC.to_string()))]
682 #[case::missing_config(Err(crate::Error::MissingConfig))]
683 #[case::repo_init_error(Err(crate::Error::UserRepoNotInitialized))]
684 #[case::user_init_error(Err(crate::Error::UserNotInitialized))]
685 #[tokio::test]
686 async fn test_create_wallet_from_new_mnemonic(#[case] expected: Result<String>) {
687 let (_srv, config, _cleanup) = set_config().await;
689 let mut sdk = Sdk::new(config).unwrap();
690
691 match &expected {
692 Ok(_) => {
693 sdk.repo = Some(Box::new(MockUserRepo::new()));
694 let mut mock_wallet_manager = MockWalletManager::new();
695 mock_wallet_manager
696 .expect_create_wallet_from_new_mnemonic()
697 .once()
698 .returning(|_, _, _, _| Ok(MNEMONIC.to_string()));
699 sdk.active_user = Some(crate::types::users::ActiveUser {
700 username: USERNAME.into(),
701 wallet_manager: Box::new(mock_wallet_manager),
702 mnemonic_derivation_options: Default::default(),
703 });
704 }
705 Err(error) => {
706 handle_error_test_cases(error, &mut sdk, 0, 0).await;
707 }
708 }
709
710 let response = sdk.create_wallet_from_new_mnemonic(&PIN).await;
712
713 match expected {
715 Ok(resp) => {
716 assert_eq!(response.unwrap(), resp);
717 }
718 Err(ref expected_err) => {
719 assert_eq!(response.err().unwrap().to_string(), expected_err.to_string());
720 }
721 }
722 }
723
724 #[rstest]
725 #[case::success(Ok(()))]
726 #[case::missing_config(Err(crate::Error::MissingConfig))]
727 #[case::repo_init_error(Err(crate::Error::UserRepoNotInitialized))]
728 #[case::user_init_error(Err(crate::Error::UserNotInitialized))]
729 #[tokio::test]
730 async fn test_create_wallet_from_existing_mnemonic(#[case] expected: Result<()>) {
731 let (_srv, config, _cleanup) = set_config().await;
733 let mut sdk = Sdk::new(config).unwrap();
734
735 match &expected {
736 Ok(_) => {
737 sdk.repo = Some(Box::new(MockUserRepo::new()));
738 let mut mock_wallet_manager = MockWalletManager::new();
739 mock_wallet_manager
740 .expect_create_wallet_from_existing_mnemonic()
741 .once()
742 .returning(|_, _, _, _, _| Ok(()));
743 sdk.active_user = Some(crate::types::users::ActiveUser {
744 username: USERNAME.into(),
745 wallet_manager: Box::new(mock_wallet_manager),
746 mnemonic_derivation_options: Default::default(),
747 });
748 }
749 Err(error) => {
750 handle_error_test_cases(error, &mut sdk, 0, 0).await;
751 }
752 }
753
754 let response = sdk.create_wallet_from_existing_mnemonic(&PIN, MNEMONIC).await;
756
757 match expected {
759 Ok(()) => response.unwrap(),
760 Err(ref expected_err) => {
761 assert_eq!(response.err().unwrap().to_string(), expected_err.to_string());
762 }
763 }
764 }
765
766 #[rstest]
767 #[case::success(Ok(BACKUP.to_vec()))]
768 #[case::missing_config(Err(crate::Error::MissingConfig))]
769 #[case::repo_init_error(Err(crate::Error::UserRepoNotInitialized))]
770 #[case::user_init_error(Err(crate::Error::UserNotInitialized))]
771 #[tokio::test]
772 async fn test_create_wallet_backup(#[case] expected: Result<Vec<u8>>) {
773 let (_srv, config, _cleanup) = set_config().await;
775 let mut sdk = Sdk::new(config).unwrap();
776
777 match &expected {
778 Ok(_) => {
779 sdk.repo = Some(Box::new(MockUserRepo::new()));
780 let mut mock_wallet_manager = MockWalletManager::new();
781 mock_wallet_manager
782 .expect_create_wallet_backup()
783 .once()
784 .returning(|_, _, _, _, _| Ok(BACKUP.to_vec()));
785 sdk.active_user = Some(crate::types::users::ActiveUser {
786 username: USERNAME.into(),
787 wallet_manager: Box::new(mock_wallet_manager),
788 mnemonic_derivation_options: Default::default(),
789 });
790 }
791 Err(error) => {
792 handle_error_test_cases(error, &mut sdk, 0, 0).await;
793 }
794 }
795
796 let response = sdk.create_wallet_backup(&PIN, &WALLET_PASSWORD).await;
798
799 match expected {
801 Ok(resp) => {
802 assert_eq!(response.unwrap(), resp);
803 }
804 Err(ref expected_err) => {
805 assert_eq!(response.err().unwrap().to_string(), expected_err.to_string());
806 }
807 }
808 }
809
810 #[rstest]
811 #[case::success(Ok(()))]
812 #[case::missing_config(Err(crate::Error::MissingConfig))]
813 #[case::repo_init_error(Err(crate::Error::UserRepoNotInitialized))]
814 #[case::user_init_error(Err(crate::Error::UserNotInitialized))]
815 #[tokio::test]
816 async fn test_create_wallet_from_backup(#[case] expected: Result<()>) {
817 let (_srv, config, _cleanup) = set_config().await;
819 let mut sdk = Sdk::new(config).unwrap();
820
821 match &expected {
822 Ok(_) => {
823 sdk.repo = Some(Box::new(MockUserRepo::new()));
824 let mut mock_wallet_manager = MockWalletManager::new();
825 mock_wallet_manager
826 .expect_create_wallet_from_backup()
827 .once()
828 .returning(|_, _, _, _, _, _| Ok(()));
829 sdk.active_user = Some(crate::types::users::ActiveUser {
830 username: USERNAME.into(),
831 wallet_manager: Box::new(mock_wallet_manager),
832 mnemonic_derivation_options: Default::default(),
833 });
834 }
835 Err(error) => {
836 handle_error_test_cases(error, &mut sdk, 0, 0).await;
837 }
838 }
839
840 let response = sdk.create_wallet_from_backup(&PIN, BACKUP, &WALLET_PASSWORD).await;
842
843 match expected {
845 Ok(()) => response.unwrap(),
846 Err(ref expected_err) => {
847 assert_eq!(response.err().unwrap().to_string(), expected_err.to_string());
848 }
849 }
850 }
851
852 #[rstest]
853 #[case::success(Ok(true))]
854 #[case::missing_config(Err(crate::Error::MissingConfig))]
855 #[case::repo_init_error(Err(crate::Error::UserRepoNotInitialized))]
856 #[case::user_init_error(Err(crate::Error::UserNotInitialized))]
857 #[tokio::test]
858 async fn test_verify_mnemonic(#[case] expected: Result<bool>) {
859 let (_srv, config, _cleanup) = set_config().await;
861 let mut sdk = Sdk::new(config).unwrap();
862
863 match &expected {
864 Ok(_) => {
865 sdk.repo = Some(Box::new(MockUserRepo::new()));
866 let mut mock_wallet_manager = MockWalletManager::new();
867 mock_wallet_manager
868 .expect_check_mnemonic()
869 .once()
870 .returning(|_, _, _, _, _| Ok(true));
871 sdk.active_user = Some(crate::types::users::ActiveUser {
872 username: USERNAME.into(),
873 wallet_manager: Box::new(mock_wallet_manager),
874 mnemonic_derivation_options: Default::default(),
875 });
876 }
877 Err(error) => {
878 handle_error_test_cases(error, &mut sdk, 0, 0).await;
879 }
880 }
881
882 let response = sdk.verify_mnemonic(&PIN, MNEMONIC).await;
884
885 match expected {
887 Ok(resp) => {
888 assert_eq!(response.unwrap(), resp);
889 }
890 Err(ref expected_err) => {
891 assert_eq!(response.err().unwrap().to_string(), expected_err.to_string());
892 }
893 }
894 }
895
896 #[rstest]
897 #[case::success(Ok(()))]
898 #[case::missing_config(Err(crate::Error::MissingConfig))]
899 #[case::repo_init_error(Err(crate::Error::UserRepoNotInitialized))]
900 #[case::user_init_error(Err(crate::Error::UserNotInitialized))]
901 #[tokio::test]
902 async fn test_delete_wallet(#[case] expected: Result<()>) {
903 let (_srv, config, _cleanup) = set_config().await;
905 let mut sdk = Sdk::new(config).unwrap();
906
907 let pin = EncryptionPin::try_from_string("123456").unwrap();
908
909 match &expected {
910 Ok(_) => {
911 let mut mock_user_repo = example_get_user(SwapPaymentDetailKey::Iota, false, 2, KycType::Undefined);
912 mock_user_repo.expect_update().once().returning(|_| Ok(()));
913
914 sdk.repo = Some(Box::new(mock_user_repo));
915
916 let mut mock_wallet_manager = MockWalletManager::new();
917 mock_wallet_manager
918 .expect_delete_wallet()
919 .once()
920 .returning(|_, _, _| Ok(()));
921 sdk.active_user = Some(crate::types::users::ActiveUser {
922 username: USERNAME.into(),
923 wallet_manager: Box::new(mock_wallet_manager),
924 mnemonic_derivation_options: Default::default(),
925 });
926
927 let new_pin = EncryptionPin::try_from_string("123456").unwrap();
928 sdk.change_pin(&pin, &new_pin).await.unwrap();
929 }
930 Err(error) => {
931 handle_error_test_cases(error, &mut sdk, 1, 0).await;
932 }
933 }
934
935 let response = sdk.delete_wallet(&PIN).await;
937
938 match expected {
940 Ok(()) => response.unwrap(),
941 Err(ref expected_err) => {
942 assert_eq!(response.err().unwrap().to_string(), expected_err.to_string());
943 }
944 }
945 }
946
947 #[rstest]
948 #[case::success(Ok(()))]
949 #[case::repo_init_error(Err(crate::Error::UserRepoNotInitialized))]
950 #[case::user_init_error(Err(crate::Error::UserNotInitialized))]
951 #[case::wallet_not_initialized(Err(crate::Error::Wallet(WalletError::WalletNotInitialized(
952 ErrorKind::MissingPassword
953 ))))]
954 #[tokio::test]
955 async fn test_verify_pin(#[case] expected: Result<()>) {
956 let (_srv, config, _cleanup) = set_config().await;
958 let mut sdk = Sdk::new(config).unwrap();
959
960 match &expected {
961 Ok(_) => {
962 let mock_user_repo = example_get_user(SwapPaymentDetailKey::Iota, false, 1, KycType::Undefined);
963 sdk.repo = Some(Box::new(mock_user_repo));
964
965 sdk.active_user = Some(crate::types::users::ActiveUser {
966 username: USERNAME.into(),
967 wallet_manager: Box::new(MockWalletManager::new()),
968 mnemonic_derivation_options: Default::default(),
969 });
970 }
971 Err(error) => {
972 handle_error_test_cases(error, &mut sdk, 1, 0).await;
973 }
974 }
975
976 let response = sdk.verify_pin(&PIN).await;
978
979 match expected {
981 Ok(()) => response.unwrap(),
982 Err(ref expected_err) => {
983 assert_eq!(response.err().unwrap().to_string(), expected_err.to_string());
984 }
985 }
986 }
987
988 #[rstest]
989 #[case::success(Ok(()))]
990 #[case::repo_init_error(Err(crate::Error::UserRepoNotInitialized))]
991 #[case::user_init_error(Err(crate::Error::UserNotInitialized))]
992 #[case::wallet_not_initialized(Err(crate::Error::Wallet(WalletError::WalletNotInitialized(
993 ErrorKind::MissingPassword
994 ))))]
995 #[tokio::test]
996 async fn test_change_pin(#[case] expected: Result<()>) {
997 let (_srv, config, _cleanup) = set_config().await;
999 let mut sdk = Sdk::new(config).unwrap();
1000
1001 match &expected {
1002 Ok(_) => {
1003 let mut mock_user_repo = example_get_user(SwapPaymentDetailKey::Iota, false, 1, KycType::Undefined);
1004 mock_user_repo.expect_update().once().returning(|_| Ok(()));
1005 sdk.repo = Some(Box::new(mock_user_repo));
1006
1007 sdk.active_user = Some(crate::types::users::ActiveUser {
1008 username: USERNAME.into(),
1009 wallet_manager: Box::new(MockWalletManager::new()),
1010 mnemonic_derivation_options: Default::default(),
1011 });
1012 }
1013 Err(error) => {
1014 handle_error_test_cases(error, &mut sdk, 1, 0).await;
1015 }
1016 }
1017
1018 let new_pin: LazyLock<EncryptionPin> = LazyLock::new(|| EncryptionPin::try_from_string("432154").unwrap());
1020 let response = sdk.change_pin(&PIN, &new_pin).await;
1021
1022 match expected {
1024 Ok(()) => response.unwrap(),
1025 Err(ref expected_err) => {
1026 assert_eq!(response.err().unwrap().to_string(), expected_err.to_string());
1027 }
1028 }
1029 }
1030
1031 #[rstest]
1032 #[case::success(Ok(()))]
1033 #[case::repo_init_error(Err(crate::Error::UserRepoNotInitialized))]
1034 #[case::user_init_error(Err(crate::Error::UserNotInitialized))]
1035 #[tokio::test]
1036 async fn test_set_wallet_password(#[case] expected: Result<()>) {
1037 let (_srv, config, _cleanup) = set_config().await;
1039 let mut sdk = Sdk::new(config).unwrap();
1040
1041 match &expected {
1042 Ok(_) => {
1043 let mut mock_user_repo = MockUserRepo::new();
1044 mock_user_repo.expect_get().times(1).returning(move |r1| {
1045 assert_eq!(r1, USERNAME);
1046 Ok(UserEntity {
1047 user_id: None,
1048 username: USERNAME.into(),
1049 encrypted_password: None,
1050 salt: SALT.into(),
1051 is_kyc_verified: false,
1052 kyc_type: KycType::Undefined,
1053 viviswap_state: Option::None,
1054 local_share: None,
1055 wallet_transactions: Vec::new(),
1056 wallet_transactions_versioned: Vec::new(),
1057 })
1058 });
1059 mock_user_repo.expect_update().once().returning(|_| Ok(()));
1060 sdk.repo = Some(Box::new(mock_user_repo));
1061
1062 sdk.active_user = Some(crate::types::users::ActiveUser {
1063 username: USERNAME.into(),
1064 wallet_manager: Box::new(MockWalletManager::new()),
1065 mnemonic_derivation_options: Default::default(),
1066 });
1067 }
1068 Err(error) => {
1069 handle_error_test_cases(error, &mut sdk, 1, 0).await;
1070 }
1071 }
1072
1073 let response = sdk.set_wallet_password(&PIN, &WALLET_PASSWORD).await;
1075
1076 match expected {
1078 Ok(()) => response.unwrap(),
1079 Err(ref expected_err) => {
1080 assert_eq!(response.err().unwrap().to_string(), expected_err.to_string());
1081 }
1082 }
1083 }
1084
1085 #[rstest]
1086 #[case::success(Ok(ADDRESS.to_string()))]
1087 #[case::repo_init_error(Err(crate::Error::UserRepoNotInitialized))]
1088 #[case::user_init_error(Err(crate::Error::UserNotInitialized))]
1089 #[case::missing_config(Err(crate::Error::MissingConfig))]
1090 #[tokio::test]
1091 async fn test_generate_new_address(#[case] expected: Result<String>) {
1092 let (mut srv, config, _cleanup) = set_config().await;
1094 let mut sdk = Sdk::new(config).unwrap();
1095 let mut mock_server = None;
1096
1097 match &expected {
1098 Ok(_) => {
1099 let mock_user_repo = example_get_user(SwapPaymentDetailKey::Iota, false, 1, KycType::Undefined);
1100 sdk.repo = Some(Box::new(mock_user_repo));
1101
1102 let mut mock_wallet_manager = MockWalletManager::new();
1103 mock_wallet_manager.expect_try_get().returning(move |_, _, _, _, _, _| {
1104 let mut mock_wallet_user = MockWalletUser::new();
1105 mock_wallet_user
1106 .expect_get_address()
1107 .once()
1108 .returning(|| Ok(ADDRESS.to_string()));
1109 Ok(WalletBorrow::from(mock_wallet_user))
1110 });
1111 sdk.active_user = Some(crate::types::users::ActiveUser {
1112 username: USERNAME.into(),
1113 wallet_manager: Box::new(mock_wallet_manager),
1114 mnemonic_derivation_options: Default::default(),
1115 });
1116 sdk.access_token = Some(TOKEN.clone());
1117 sdk.set_networks(example_api_networks());
1118 sdk.set_network(IOTA_NETWORK_KEY.to_string()).await.unwrap();
1119
1120 let mock_request = SetUserAddressRequest {
1121 address: ADDRESS.into(),
1122 };
1123 let body = serde_json::to_string(&mock_request).unwrap();
1124
1125 mock_server = Some(
1126 srv.mock("PUT", "/api/user/address")
1127 .match_header(HEADER_X_APP_NAME, AUTH_PROVIDER)
1128 .match_header("authorization", format!("Bearer {}", TOKEN.as_str()).as_str())
1129 .match_header("content-type", "application/json")
1130 .match_query(Matcher::Exact("network_key=IOTA".to_string()))
1131 .match_body(Matcher::Exact(body))
1132 .with_status(201)
1133 .expect(1)
1134 .with_header("content-type", "application/json")
1135 .create(),
1136 );
1137 }
1138 Err(error) => {
1139 handle_error_test_cases(error, &mut sdk, 1, 0).await;
1140 }
1141 }
1142
1143 let response = sdk.generate_new_address(&PIN).await;
1145
1146 match expected {
1148 Ok(resp) => {
1149 assert_eq!(response.unwrap(), resp);
1150 }
1151 Err(ref expected_err) => {
1152 assert_eq!(response.err().unwrap().to_string(), expected_err.to_string());
1153 }
1154 }
1155 if let Some(m) = mock_server {
1156 m.assert();
1157 }
1158 }
1159
1160 #[rstest]
1161 #[case::success(Ok(unsafe { CryptoAmount::new_unchecked(dec!(25.0)) }))]
1163 #[case::repo_init_error(Err(crate::Error::UserRepoNotInitialized))]
1164 #[case::user_init_error(Err(crate::Error::UserNotInitialized))]
1165 #[case::missing_config(Err(crate::Error::MissingConfig))]
1166 #[tokio::test]
1167 async fn test_get_balance(#[case] expected: Result<CryptoAmount>) {
1168 let (_srv, config, _cleanup) = set_config().await;
1170 let mut sdk = Sdk::new(config).unwrap();
1171
1172 match &expected {
1173 Ok(_) => {
1174 let mock_user_repo = example_get_user(SwapPaymentDetailKey::Iota, false, 1, KycType::Undefined);
1175 sdk.repo = Some(Box::new(mock_user_repo));
1176
1177 let mut mock_wallet_manager = MockWalletManager::new();
1178 mock_wallet_manager.expect_try_get().returning(move |_, _, _, _, _, _| {
1179 let mut mock_wallet_user = MockWalletUser::new();
1180 mock_wallet_user
1181 .expect_get_balance()
1182 .once()
1183 .returning(|| Ok(unsafe { CryptoAmount::new_unchecked(dec!(25.0)) }));
1185 Ok(WalletBorrow::from(mock_wallet_user))
1186 });
1187 sdk.active_user = Some(crate::types::users::ActiveUser {
1188 username: USERNAME.into(),
1189 wallet_manager: Box::new(mock_wallet_manager),
1190 mnemonic_derivation_options: Default::default(),
1191 });
1192 sdk.set_networks(example_api_networks());
1193 sdk.set_network(IOTA_NETWORK_KEY.to_string()).await.unwrap();
1194 }
1195 Err(error) => {
1196 handle_error_test_cases(error, &mut sdk, 1, 0).await;
1197 }
1198 }
1199
1200 let response = sdk.get_balance(&PIN).await;
1202
1203 match expected {
1205 Ok(resp) => {
1206 assert_eq!(response.unwrap(), resp);
1207 }
1208 Err(ref expected_err) => {
1209 assert_eq!(response.err().unwrap().to_string(), expected_err.to_string());
1210 }
1211 }
1212 }
1213
1214 #[rstest]
1215 #[case::success(Ok(WalletTransaction::from(example_versioned_wallet_transaction())))]
1216 #[case::repo_init_error(Err(crate::Error::UserRepoNotInitialized))]
1217 #[case::user_init_error(Err(crate::Error::UserNotInitialized))]
1218 #[case::missing_config(Err(crate::Error::MissingConfig))]
1219 #[tokio::test]
1220 async fn test_get_wallet_tx(#[case] expected: Result<WalletTransaction>) {
1221 let (_srv, config, _cleanup) = set_config().await;
1223 let mut sdk = Sdk::new(config).unwrap();
1224
1225 match &expected {
1226 Ok(_) => {
1227 let mock_user_repo = example_get_user(SwapPaymentDetailKey::Iota, false, 1, KycType::Undefined);
1228 sdk.repo = Some(Box::new(mock_user_repo));
1229
1230 let mut mock_wallet_manager = MockWalletManager::new();
1231 mock_wallet_manager.expect_try_get().returning(move |_, _, _, _, _, _| {
1232 let mut mock_wallet_user = MockWalletUser::new();
1233 mock_wallet_user
1234 .expect_get_wallet_tx()
1235 .once()
1236 .returning(|_| Ok(WalletTransaction::from(example_versioned_wallet_transaction())));
1237 Ok(WalletBorrow::from(mock_wallet_user))
1238 });
1239 sdk.active_user = Some(crate::types::users::ActiveUser {
1240 username: USERNAME.into(),
1241 wallet_manager: Box::new(mock_wallet_manager),
1242 mnemonic_derivation_options: Default::default(),
1243 });
1244 sdk.set_networks(example_api_networks());
1245 sdk.set_network(IOTA_NETWORK_KEY.to_string()).await.unwrap();
1246 }
1247 Err(error) => {
1248 handle_error_test_cases(error, &mut sdk, 1, 0).await;
1249 }
1250 }
1251
1252 let response = sdk.get_wallet_tx(&PIN, TX_INDEX).await;
1254
1255 match expected {
1257 Ok(resp) => {
1258 assert_eq!(response.unwrap(), resp);
1259 }
1260 Err(ref expected_err) => {
1261 assert_eq!(response.err().unwrap().to_string(), expected_err.to_string());
1262 }
1263 }
1264 }
1265
1266 #[rstest]
1267 #[case::success(Ok(WalletTxInfoList { transactions: vec![]}))]
1268 #[case::repo_init_error(Err(crate::Error::UserRepoNotInitialized))]
1269 #[case::user_init_error(Err(crate::Error::UserNotInitialized))]
1270 #[case::missing_config(Err(crate::Error::MissingConfig))]
1271 #[tokio::test]
1272 async fn test_get_wallet_tx_list(#[case] expected: Result<WalletTxInfoList>) {
1273 let (_srv, config, _cleanup) = set_config().await;
1275 let mut sdk = Sdk::new(config).unwrap();
1276
1277 match &expected {
1278 Ok(_) => {
1279 let mut mock_user_repo = example_get_user(SwapPaymentDetailKey::Iota, false, 2, KycType::Undefined);
1280 mock_user_repo
1281 .expect_set_wallet_transactions()
1282 .once()
1283 .returning(|_, _| Ok(()));
1284 sdk.repo = Some(Box::new(mock_user_repo));
1285
1286 let mut mock_wallet_manager = MockWalletManager::new();
1287 mock_wallet_manager.expect_try_get().returning(move |_, _, _, _, _, _| {
1288 let mut mock_wallet_user = MockWalletUser::new();
1289 mock_wallet_user
1290 .expect_get_wallet_tx_list()
1291 .once()
1292 .returning(|_, _| Ok(vec![]));
1293 Ok(WalletBorrow::from(mock_wallet_user))
1294 });
1295 sdk.active_user = Some(crate::types::users::ActiveUser {
1296 username: USERNAME.into(),
1297 wallet_manager: Box::new(mock_wallet_manager),
1298 mnemonic_derivation_options: Default::default(),
1299 });
1300 sdk.set_networks(example_api_networks());
1301 sdk.set_network(IOTA_NETWORK_KEY.to_string()).await.unwrap();
1302 }
1303 Err(error) => {
1304 handle_error_test_cases(error, &mut sdk, 1, 0).await;
1305 }
1306 }
1307
1308 let response = sdk.get_wallet_tx_list(&PIN, 0, 10).await;
1310
1311 match expected {
1313 Ok(resp) => {
1314 assert_eq!(response.unwrap(), resp);
1315 }
1316 Err(ref expected_err) => {
1317 assert_eq!(response.err().unwrap().to_string(), expected_err.to_string());
1318 }
1319 }
1320 }
1321
1322 fn mock_wallet_transaction(
1323 hash: String,
1324 status: WalletTxStatus,
1325 network_key: String,
1326 date: DateTime<Utc>,
1327 ) -> WalletTransaction {
1328 WalletTransaction {
1329 date,
1330 block_number_hash: None,
1331 transaction_hash: hash,
1332 receiver: String::new(),
1333 sender: String::new(),
1334 amount: unsafe { CryptoAmount::new_unchecked(dec!(20.0)) },
1335 network_key,
1336 status,
1337 explorer_url: None,
1338 gas_fee: None,
1339 is_sender: true,
1340 }
1341 }
1342
1343 #[tokio::test]
1344 async fn test_get_wallet_tx_list_filters_transactions_correctly() {
1345 let (_srv, config, _cleanup) = set_config().await;
1347 let mut sdk = Sdk::new(config).unwrap();
1348 let mock_date = Utc::now();
1349
1350 let wallet_transactions_versioned = vec![
1354 VersionedWalletTransaction::from(mock_wallet_transaction(
1355 String::from("some tx id"),
1356 WalletTxStatus::Confirmed,
1357 String::from("IOTA"),
1358 mock_date,
1359 )),
1360 VersionedWalletTransaction::from(mock_wallet_transaction(
1361 String::from("1"),
1362 WalletTxStatus::Pending,
1363 String::from("ETH"),
1364 mock_date,
1365 )),
1366 VersionedWalletTransaction::from(mock_wallet_transaction(
1367 String::from("2"),
1368 WalletTxStatus::Pending, String::from("ETH"),
1370 mock_date,
1371 )),
1372 VersionedWalletTransaction::from(mock_wallet_transaction(
1373 String::from("3"),
1374 WalletTxStatus::Pending,
1375 String::from("ETH"),
1376 mock_date,
1377 )),
1378 ];
1379
1380 let mut mock_user_repo = MockUserRepo::new();
1381 mock_user_repo.expect_get().returning(move |_| {
1382 Ok(UserEntity {
1383 user_id: None,
1384 username: USERNAME.to_string(),
1385 encrypted_password: Some(ENCRYPTED_WALLET_PASSWORD.clone()),
1386 salt: SALT.into(),
1387 is_kyc_verified: false,
1388 kyc_type: KycType::Undefined,
1389 viviswap_state: None,
1390 local_share: None,
1391 wallet_transactions: Vec::new(),
1392 wallet_transactions_versioned: wallet_transactions_versioned.clone(),
1393 })
1394 });
1395
1396 let expected = vec![
1397 VersionedWalletTransaction::from(mock_wallet_transaction(
1398 String::from("some tx id"),
1399 WalletTxStatus::Confirmed,
1400 String::from("IOTA"),
1401 mock_date,
1402 )),
1403 VersionedWalletTransaction::from(mock_wallet_transaction(
1404 String::from("1"),
1405 WalletTxStatus::Pending,
1406 String::from("ETH"),
1407 mock_date,
1408 )),
1409 VersionedWalletTransaction::from(mock_wallet_transaction(
1410 String::from("2"),
1411 WalletTxStatus::Confirmed, String::from("ETH"),
1413 mock_date,
1414 )),
1415 VersionedWalletTransaction::from(mock_wallet_transaction(
1416 String::from("3"),
1417 WalletTxStatus::Pending,
1418 String::from("ETH"),
1419 mock_date,
1420 )),
1421 ];
1422
1423 mock_user_repo
1424 .expect_set_wallet_transactions()
1425 .once()
1426 .with(eq(USERNAME.to_string()), eq(expected.clone()))
1427 .returning(|_, _| Ok(()));
1428
1429 sdk.repo = Some(Box::new(mock_user_repo));
1430
1431 let mut mock_wallet_manager = MockWalletManager::new();
1432 mock_wallet_manager.expect_try_get().returning(move |_, _, _, _, _, _| {
1433 let mut mock_wallet_user = MockWalletUser::new();
1434 mock_wallet_user
1435 .expect_get_wallet_tx_list()
1436 .once()
1437 .returning(|_, _| Ok(vec![]));
1438 mock_wallet_user
1439 .expect_get_wallet_tx()
1440 .once()
1441 .with(eq(String::from("2"))) .returning(move |_| {
1443 Ok(mock_wallet_transaction(
1444 String::from("2"),
1445 WalletTxStatus::Confirmed, String::from("ETH"),
1447 mock_date,
1448 ))
1449 });
1450 Ok(WalletBorrow::from(mock_wallet_user))
1451 });
1452
1453 sdk.active_user = Some(crate::types::users::ActiveUser {
1454 username: USERNAME.into(),
1455 wallet_manager: Box::new(mock_wallet_manager),
1456 mnemonic_derivation_options: Default::default(),
1457 });
1458
1459 sdk.set_networks(example_api_networks());
1460 sdk.set_network(ETH_NETWORK_KEY.to_string()).await.unwrap();
1461
1462 let response = sdk.get_wallet_tx_list(&PIN, 1, 1).await.unwrap();
1470
1471 assert_eq!(
1473 response,
1474 WalletTxInfoList {
1475 transactions: vec![mock_wallet_transaction(
1476 String::from("2"),
1477 WalletTxStatus::Confirmed, String::from("ETH"),
1479 mock_date,
1480 )]
1481 }
1482 );
1483 }
1484
1485 #[tokio::test]
1486 async fn test_get_wallet_tx_list_does_not_query_network_for_transaction_state() {
1487 let (_srv, config, _cleanup) = set_config().await;
1489 let mut sdk = Sdk::new(config).unwrap();
1490
1491 let wallet_transactions = vec![VersionedWalletTransaction::from(mock_wallet_transaction(
1492 String::from("1"),
1493 WalletTxStatus::Confirmed,
1494 String::from("ETH"),
1495 Utc::now(),
1496 ))];
1497
1498 let mut mock_user_repo = MockUserRepo::new();
1499 mock_user_repo.expect_get().returning(move |_| {
1500 Ok(UserEntity {
1501 user_id: None,
1502 username: USERNAME.to_string(),
1503 encrypted_password: Some(ENCRYPTED_WALLET_PASSWORD.clone()),
1504 salt: SALT.into(),
1505 is_kyc_verified: false,
1506 kyc_type: KycType::Undefined,
1507 viviswap_state: None,
1508 local_share: None,
1509 wallet_transactions: Vec::new(),
1510 wallet_transactions_versioned: wallet_transactions.clone(),
1511 })
1512 });
1513
1514 mock_user_repo
1515 .expect_set_wallet_transactions()
1516 .once()
1517 .returning(|_, _| Ok(()));
1518
1519 sdk.repo = Some(Box::new(mock_user_repo));
1520
1521 let mut mock_wallet_manager = MockWalletManager::new();
1522 mock_wallet_manager.expect_try_get().returning(move |_, _, _, _, _, _| {
1523 let mut mock_wallet_user = MockWalletUser::new();
1524 mock_wallet_user
1525 .expect_get_wallet_tx_list()
1526 .once()
1527 .returning(|_, _| Ok(vec![]));
1528 mock_wallet_user.expect_get_wallet_tx().never();
1529 Ok(WalletBorrow::from(mock_wallet_user))
1530 });
1531
1532 sdk.active_user = Some(crate::types::users::ActiveUser {
1533 username: USERNAME.into(),
1534 wallet_manager: Box::new(mock_wallet_manager),
1535 mnemonic_derivation_options: Default::default(),
1536 });
1537
1538 sdk.set_networks(example_api_networks());
1539 sdk.set_network(ETH_NETWORK_KEY.to_string()).await.unwrap();
1540
1541 let response = sdk.get_wallet_tx_list(&PIN, 0, 1).await;
1543
1544 assert!(response.is_ok())
1546 }
1547
1548 #[tokio::test]
1549 async fn test_get_wallet_tx_list_should_sort_wallet_transactions() {
1550 let (_srv, config, _cleanup) = set_config().await;
1552 let mut sdk = Sdk::new(config).unwrap();
1553
1554 let tx_3 = VersionedWalletTransaction::from(mock_wallet_transaction(
1555 String::from("3"),
1556 WalletTxStatus::Confirmed,
1557 String::from("ETH"),
1558 Utc.with_ymd_and_hms(2025, 5, 29, 8, 37, 15).unwrap(),
1559 ));
1560
1561 let tx_1 = VersionedWalletTransaction::from(mock_wallet_transaction(
1562 String::from("1"),
1563 WalletTxStatus::Confirmed,
1564 String::from("ETH"),
1565 Utc.with_ymd_and_hms(2025, 5, 29, 8, 37, 13).unwrap(),
1566 ));
1567
1568 let tx_2 = VersionedWalletTransaction::from(mock_wallet_transaction(
1569 String::from("2"),
1570 WalletTxStatus::Confirmed,
1571 String::from("ETH"),
1572 Utc.with_ymd_and_hms(2025, 5, 29, 8, 37, 14).unwrap(),
1573 ));
1574
1575 let wallet_transactions = vec![tx_3.clone(), tx_1.clone(), tx_2.clone()];
1576 let expected = vec![
1577 WalletTransaction::from(tx_3),
1578 WalletTransaction::from(tx_2),
1579 WalletTransaction::from(tx_1),
1580 ];
1581
1582 let mut mock_user_repo = MockUserRepo::new();
1583 mock_user_repo.expect_get().returning(move |_| {
1584 Ok(UserEntity {
1585 user_id: None,
1586 username: USERNAME.to_string(),
1587 encrypted_password: Some(ENCRYPTED_WALLET_PASSWORD.clone()),
1588 salt: SALT.into(),
1589 is_kyc_verified: false,
1590 kyc_type: KycType::Undefined,
1591 viviswap_state: None,
1592 local_share: None,
1593 wallet_transactions: Vec::new(),
1594 wallet_transactions_versioned: wallet_transactions.clone(),
1595 })
1596 });
1597
1598 mock_user_repo
1599 .expect_set_wallet_transactions()
1600 .once()
1601 .returning(|_, _| Ok(()));
1602
1603 sdk.repo = Some(Box::new(mock_user_repo));
1604
1605 let mut mock_wallet_manager = MockWalletManager::new();
1606 mock_wallet_manager.expect_try_get().returning(move |_, _, _, _, _, _| {
1607 let mut mock_wallet_user = MockWalletUser::new();
1608 mock_wallet_user
1609 .expect_get_wallet_tx_list()
1610 .once()
1611 .returning(|_, _| Ok(vec![]));
1612 mock_wallet_user.expect_get_wallet_tx().never();
1613 Ok(WalletBorrow::from(mock_wallet_user))
1614 });
1615
1616 sdk.active_user = Some(crate::types::users::ActiveUser {
1617 username: USERNAME.into(),
1618 wallet_manager: Box::new(mock_wallet_manager),
1619 mnemonic_derivation_options: Default::default(),
1620 });
1621
1622 sdk.set_networks(example_api_networks());
1623 sdk.set_network(ETH_NETWORK_KEY.to_string()).await.unwrap();
1624
1625 let response = sdk.get_wallet_tx_list(&PIN, 0, 5).await;
1627
1628 assert_eq!(expected, response.unwrap().transactions)
1630 }
1631}