1use super::Sdk;
2use crate::backend::transactions::{
3 commit_transaction, create_new_transaction, get_transaction_details, get_transactions_list,
4};
5use crate::error::Result;
6use crate::types::currencies::CryptoAmount;
7use crate::types::transactions::{GasCostEstimation, PurchaseDetails};
8use crate::types::{
9 newtypes::EncryptionPin,
10 transactions::{TxInfo, TxList},
11};
12use crate::wallet::error::WalletError;
13use crate::wallet::wallet::TransactionIntent;
14use api_types::api::networks::ApiProtocol;
15use api_types::api::transactions::{ApiApplicationMetadata, ApiTxStatus, PurchaseModel, Reason};
16use log::{debug, info};
17
18impl Sdk {
19 pub async fn create_purchase_request(
37 &self,
38 receiver: &str,
39 amount: CryptoAmount,
40 product_hash: &str,
41 app_data: &str,
42 purchase_type: &str,
43 ) -> Result<String> {
44 info!("Creating a new purchase request");
45 let Some(_active_user) = &self.active_user else {
46 return Err(crate::Error::UserNotInitialized);
47 };
48
49 let config = self.config.as_ref().ok_or(crate::Error::MissingConfig)?;
50 let access_token = self
51 .access_token
52 .as_ref()
53 .ok_or(crate::error::Error::MissingAccessToken)?;
54 let network = self.active_network.clone().ok_or(crate::Error::MissingNetwork)?;
55
56 let purchase_model = PurchaseModel::try_from(purchase_type.to_string()).map_err(crate::error::Error::Parse)?;
57
58 let reason = match purchase_model {
59 PurchaseModel::CLIK => Reason::LIKE,
60 PurchaseModel::CPIC => Reason::PURCHASE,
61 };
62
63 let metadata = ApiApplicationMetadata {
64 product_hash: product_hash.into(),
65 reason: reason.to_string(),
66 purchase_model: purchase_model.to_string(),
67 app_data: app_data.into(),
68 };
69 let response = create_new_transaction(config, access_token, receiver, network.key, amount, metadata).await?;
70 let purchase_id = response.index;
71 debug!("Created purchase request with id: {purchase_id}");
72 Ok(purchase_id)
73 }
74
75 pub async fn get_purchase_details(&self, purchase_id: &str) -> Result<PurchaseDetails> {
89 info!("Getting purchase details with id {purchase_id}");
90 let Some(_active_user) = &self.active_user else {
91 return Err(crate::Error::UserNotInitialized);
92 };
93
94 let access_token = self
95 .access_token
96 .as_ref()
97 .ok_or(crate::error::Error::MissingAccessToken)?;
98
99 let config = self.config.as_ref().ok_or(crate::Error::MissingConfig)?;
100 let response = get_transaction_details(config, access_token, purchase_id).await?;
101
102 let details = PurchaseDetails {
103 system_address: response.system_address,
104 amount: response.amount.try_into()?,
105 status: response.status,
106 network: response.network,
107 };
108 Ok(details)
109 }
110
111 pub async fn confirm_purchase_request(&mut self, pin: &EncryptionPin, purchase_id: &str) -> Result<()> {
127 info!("Confirming purchase request with id {purchase_id}");
128 self.verify_pin(pin).await?;
129
130 let Some(repo) = &mut self.repo else {
131 return Err(crate::Error::UserRepoNotInitialized);
132 };
133 let Some(active_user) = &mut self.active_user else {
134 return Err(crate::Error::UserNotInitialized);
135 };
136
137 let config = self.config.as_mut().ok_or(crate::Error::MissingConfig)?;
138 let access_token = self
139 .access_token
140 .as_ref()
141 .ok_or(crate::error::Error::MissingAccessToken)?;
142 let tx_details = get_transaction_details(config, access_token, purchase_id).await?;
143
144 debug!("Tx details: {:?}", tx_details);
145
146 if tx_details.status != ApiTxStatus::Valid {
147 return Err(WalletError::InvalidTransaction(format!(
148 "Transaction is not valid, current status: {}.",
149 tx_details.status
150 )))?;
151 }
152
153 let current_network = self.active_network.as_ref().ok_or(crate::Error::MissingNetwork)?;
154
155 let network = &tx_details.network;
158 if network.key != current_network.key {
159 return Err(WalletError::InvalidTransaction(format!(
160 "Transaction to commit is in network_key {:?}, but {:?} is the currently active current_network_key.",
161 network.key, current_network.key
162 )))?;
163 }
164
165 let wallet = active_user
166 .wallet_manager
167 .try_get(config, &self.access_token, repo, network, pin)
168 .await?;
169
170 let amount = tx_details.amount.try_into()?;
171
172 let intent = TransactionIntent {
173 address_to: tx_details.system_address.clone(),
174 amount,
175 data: Some(purchase_id.to_string().into_bytes()),
176 };
177
178 let tx_id = wallet.send_amount(&intent).await?;
179
180 if let ApiProtocol::Evm { .. } = tx_details.network.protocol {
183 let tx_id = wallet.send_amount(&intent).await?;
184
185 let newly_created_transaction = wallet.get_wallet_tx(&tx_id).await?;
186 let mut user = repo.get(&active_user.username)?;
187 user.wallet_transactions.push(newly_created_transaction);
188 let _ = repo.set_wallet_transactions(&active_user.username, user.wallet_transactions);
189 }
190
191 if let ApiProtocol::EvmERC20 { .. } = tx_details.network.protocol {
192 let tx_id = wallet.send_amount(&intent).await?;
193
194 let newly_created_transaction = wallet.get_wallet_tx(&tx_id).await?;
195 let mut user = repo.get(&active_user.username)?;
196 user.wallet_transactions.push(newly_created_transaction);
197 let _ = repo.set_wallet_transactions(&active_user.username, user.wallet_transactions);
198 }
199
200 debug!("Transaction id on network: {tx_id}");
201
202 commit_transaction(config, access_token, purchase_id, &tx_id).await?;
203
204 Ok(())
205 }
206
207 pub async fn send_amount(
225 &mut self,
226 pin: &EncryptionPin,
227 address: &str,
228 amount: CryptoAmount,
229 data: Option<Vec<u8>>,
230 ) -> Result<String> {
231 info!("Sending amount {amount:?} to receiver {address}");
232 self.verify_pin(pin).await?;
233
234 let Some(repo) = &mut self.repo else {
235 return Err(crate::Error::UserRepoNotInitialized);
236 };
237 let Some(active_user) = &mut self.active_user else {
238 return Err(crate::Error::UserNotInitialized);
239 };
240
241 let config = self.config.as_mut().ok_or(crate::Error::MissingConfig)?;
242 let network = self.active_network.as_ref().ok_or(crate::Error::MissingNetwork)?;
243
244 let wallet = active_user
245 .wallet_manager
246 .try_get(config, &self.access_token, repo, network, pin)
247 .await?;
248
249 let intent = TransactionIntent {
251 address_to: address.to_string(),
252 amount,
253 data,
254 };
255
256 let tx_id = match network.protocol {
257 ApiProtocol::EvmERC20 {
258 chain_id: _,
259 contract_address: _,
260 } => wallet.send_amount(&intent).await?,
261 ApiProtocol::Evm { chain_id: _ } => {
262 let tx_id = wallet.send_amount(&intent).await?;
263
264 let newly_created_transaction = wallet.get_wallet_tx(&tx_id).await?;
266 let user = repo.get(&active_user.username)?;
267 let mut wallet_transactions = user.wallet_transactions;
268 wallet_transactions.push(newly_created_transaction);
269 let _ = repo.set_wallet_transactions(&active_user.username, wallet_transactions);
270 tx_id
271 }
272 ApiProtocol::Stardust {} => wallet.send_amount(&intent).await?,
273 };
274
275 Ok(tx_id)
276 }
277
278 pub async fn estimate_gas(
296 &mut self,
297 pin: &EncryptionPin,
298 address: &str,
299 amount: CryptoAmount,
300 data: Option<Vec<u8>>,
301 ) -> Result<GasCostEstimation> {
302 info!("Estimating gas for sending amount {amount:?} to receiver {address}");
303 self.verify_pin(pin).await?;
304
305 let Some(repo) = &mut self.repo else {
306 return Err(crate::Error::UserRepoNotInitialized);
307 };
308 let Some(active_user) = &mut self.active_user else {
309 return Err(crate::Error::UserNotInitialized);
310 };
311
312 let config = self.config.as_mut().ok_or(crate::Error::MissingConfig)?;
313 let network = self.active_network.as_ref().ok_or(crate::Error::MissingNetwork)?;
314
315 let wallet = active_user
316 .wallet_manager
317 .try_get(config, &self.access_token, repo, network, pin)
318 .await?;
319
320 let intent = TransactionIntent {
322 address_to: address.to_string(),
323 amount,
324 data,
325 };
326
327 let estimate = wallet.estimate_gas_cost(&intent).await?;
328 info!("Estimate: {estimate:?}");
329
330 Ok(estimate)
331 }
332 pub async fn get_tx_list(&self, start: u32, limit: u32) -> Result<TxList> {
347 info!("Getting list of transactions");
348 let config = self.config.as_ref().ok_or(crate::Error::MissingConfig)?;
349
350 let user = self.get_user().await?;
351
352 let access_token = self
353 .access_token
354 .as_ref()
355 .ok_or(crate::error::Error::MissingAccessToken)?;
356 let txs_list = get_transactions_list(config, access_token, start, limit).await?;
357 log::debug!("Txs list for user {}: {:?}", user.username, txs_list);
358
359 Ok(TxList {
360 txs: txs_list
361 .txs
362 .into_iter()
363 .map(|val| {
364 Ok(TxInfo {
365 date: Some(val.created_at),
366 sender: val.incoming.username,
367 receiver: val.outgoing.username,
368 reference_id: val.index,
369 amount: val.incoming.amount.0.try_into()?,
370 currency: val.incoming.network.display_symbol,
371 application_metadata: val.application_metadata,
372 status: val.status,
373 transaction_hash: val.incoming.transaction_id,
374 course: val.incoming.exchange_rate.0.try_into()?,
375 })
376 })
377 .collect::<Result<Vec<_>>>()?,
378 })
379 }
380}
381
382#[cfg(test)]
383mod tests {
384 use super::*;
385 use crate::core::core_testing_utils::handle_error_test_cases;
386 use crate::testing_utils::{
387 AUTH_PROVIDER, ETH_NETWORK_KEY, HEADER_X_APP_NAME, IOTA_NETWORK_KEY, PURCHASE_ID, TOKEN, TX_INDEX, USERNAME,
388 example_api_network, example_api_networks, example_get_user, example_tx_details, example_tx_metadata,
389 example_wallet_borrow, set_config,
390 };
391 use crate::types::transactions::WalletTxInfo;
392 use crate::types::users::KycType;
393 use crate::{
394 core::Sdk,
395 user::MockUserRepo,
396 wallet::wallet::MockWalletUser,
397 wallet_manager::{MockWalletManager, WalletBorrow},
398 };
399 use api_types::api::transactions::GetTxsDetailsResponse;
400 use api_types::api::transactions::{
401 ApiTransaction, ApiTransferDetails, CreateTransactionResponse, GetTransactionDetailsResponse,
402 };
403 use api_types::api::viviswap::detail::SwapPaymentDetailKey;
404 use iota_sdk::wallet::account::types::InclusionState;
405 use mockito::Matcher;
406 use rstest::rstest;
407 use rust_decimal_macros::dec;
408
409 fn examples_wallet_tx_list() -> GetTxsDetailsResponse {
410 let main_address = "atoi1qzt0nhsf38nh6rs4p6zs5knqp6psgha9wsv74uajqgjmwc75ugupx3y7x0r".to_string();
411 let aux_address = "atoi1qpnrumvaex24dy0duulp4q07lpa00w20ze6jfd0xly422kdcjxzakzsz5kf".to_string();
412
413 GetTxsDetailsResponse {
414 txs: vec![ApiTransaction {
415 index: "1127f4ba-a0b8-4ecc-a928-bbebc401ac1a".to_string(),
416 status: ApiTxStatus::Completed,
417 created_at: "2022-12-09T09:30:33.52Z".to_string(),
418 updated_at: "2022-12-09T09:30:33.52Z".to_string(),
419 fee_rate: dec!(0.2).into(),
420 incoming: ApiTransferDetails {
421 transaction_id: Some(
422 "0x215322f8afdba4e22463a9d8a2e25d96ab0cb9ae6d56ee5ab13065068dae46c0".to_string(),
423 ),
424 block_id: Some("0x215322f8afdba4e22463a9d8a2e25d96ab0cb9ae6d56ee5ab13065068dae46c0".to_string()),
425 username: "satoshi".into(),
426 address: main_address.clone(),
427 amount: dec!(920.89).into(),
428 exchange_rate: dec!(0.06015).into(),
429 network: example_api_network(IOTA_NETWORK_KEY.to_string()),
430 },
431 outgoing: ApiTransferDetails {
432 transaction_id: Some(
433 "0x215322f8afdba4e22463a9d8a2e25d96ab0cb9ae6d56ee5ab13065068dae46c0".to_string(),
434 ),
435 block_id: Some("0x215322f8afdba4e22463a9d8a2e25d96ab0cb9ae6d56ee5ab13065068dae46c0".to_string()),
436 username: "hulk".into(),
437 address: aux_address.clone(),
438 amount: dec!(920.89).into(),
439 exchange_rate: dec!(0.06015).into(),
440 network: example_api_network(IOTA_NETWORK_KEY.to_string()),
441 },
442 application_metadata: Some(example_tx_metadata()),
443 }],
444 }
445 }
446
447 #[rstest]
448 #[case::success(Ok(CreateTransactionResponse { index: TX_INDEX.into() }))]
449 #[case::user_init_error(Err(crate::Error::UserNotInitialized))]
450 #[case::unauthorized(Err(crate::Error::MissingAccessToken))]
451 #[case::missing_config(Err(crate::Error::MissingConfig))]
452 #[tokio::test]
453 async fn test_create_purchase_request(#[case] expected: Result<CreateTransactionResponse>) {
454 let (mut srv, config, _cleanup) = set_config().await;
456 let mut sdk = Sdk::new(config).unwrap();
457 sdk.set_networks(example_api_networks());
458 sdk.set_network(IOTA_NETWORK_KEY.to_string()).await.unwrap();
459 let mut mock_server = None;
460
461 match &expected {
462 Ok(_) => {
463 sdk.active_user = Some(crate::types::users::ActiveUser {
464 username: USERNAME.into(),
465 wallet_manager: Box::new(MockWalletManager::new()),
466 });
467 sdk.access_token = Some(TOKEN.clone());
468
469 let mock_response = CreateTransactionResponse { index: TX_INDEX.into() };
470 let body = serde_json::to_string(&mock_response).unwrap();
471
472 mock_server = Some(
473 srv.mock("POST", "/api/transactions/create")
474 .match_header(HEADER_X_APP_NAME, AUTH_PROVIDER)
475 .match_header("authorization", format!("Bearer {}", TOKEN.as_str()).as_str())
476 .with_status(201)
477 .with_header("content-type", "application/json")
478 .with_body(body)
479 .expect(1)
480 .create(),
481 );
482 }
483 Err(error) => {
484 handle_error_test_cases(error, &mut sdk, 0, 0).await;
485 }
486 }
487
488 let amount = CryptoAmount::try_from(dec!(10.0)).unwrap();
490 let response = sdk
491 .create_purchase_request("receiver", amount, "hash", "app_data", "CLIK")
492 .await;
493
494 match expected {
496 Ok(resp) => {
497 assert_eq!(response.unwrap(), resp.index);
498 }
499 Err(ref expected_err) => {
500 assert_eq!(response.err().unwrap().to_string(), expected_err.to_string());
501 }
502 }
503 if let Some(m) = mock_server {
504 m.assert();
505 }
506 }
507
508 #[rstest]
509 #[case::success(Ok(()))]
510 #[case::repo_init_error(Err(crate::Error::UserRepoNotInitialized))]
511 #[case::user_init_error(Err(crate::Error::UserNotInitialized))]
512 #[case::unauthorized(Err(crate::Error::MissingAccessToken))]
513 #[case::missing_config(Err(crate::Error::MissingConfig))]
514 #[case::invalid_tx(Err(crate::Error::Wallet(WalletError::InvalidTransaction(format!(
515 "Transaction is not valid, current status: {}.",
516 ApiTxStatus::Invalid(vec!["ReceiverNotVerified".to_string()])
517 )))))]
518 #[tokio::test]
519 async fn test_commit_transaction(#[case] expected: Result<()>) {
520 let (mut srv, config, _cleanup) = set_config().await;
522 let mut sdk = Sdk::new(config).unwrap();
523 sdk.set_networks(example_api_networks());
524 sdk.set_network(IOTA_NETWORK_KEY.to_string()).await.unwrap();
525 let mut mock_server_details = None;
526 let mut mock_server_commit = None;
527
528 match &expected {
529 Ok(_) => {
530 let mock_user_repo = example_get_user(SwapPaymentDetailKey::Iota, false, 1, KycType::Undefined);
531 sdk.repo = Some(Box::new(mock_user_repo));
532
533 let mut mock_wallet_manager = MockWalletManager::new();
534 mock_wallet_manager.expect_try_get().returning(move |_, _, _, _, _| {
535 let mut mock_wallet_user = MockWalletUser::new();
536 mock_wallet_user
537 .expect_send_amount()
538 .once()
539 .returning(|_| Ok("tx_id".to_string()));
540
541 Ok(WalletBorrow::from(mock_wallet_user))
542 });
543 sdk.active_user = Some(crate::types::users::ActiveUser {
544 username: USERNAME.into(),
545 wallet_manager: Box::new(mock_wallet_manager),
546 });
547
548 sdk.access_token = Some(TOKEN.clone());
549
550 let mock_tx_response = GetTransactionDetailsResponse {
551 system_address: "".to_string(),
552 amount: dec!(5.0).into(),
553 status: ApiTxStatus::Valid,
554 network: example_api_network(IOTA_NETWORK_KEY.to_string()),
555 };
556 let body = serde_json::to_string(&mock_tx_response).unwrap();
557
558 mock_server_details = Some(
559 srv.mock("GET", "/api/transactions/details?index=123")
560 .match_header(HEADER_X_APP_NAME, AUTH_PROVIDER)
561 .match_header("authorization", format!("Bearer {}", TOKEN.as_str()).as_str())
562 .with_status(200)
563 .with_body(&body)
564 .with_header("content-type", "application/json")
565 .create(),
566 );
567
568 mock_server_commit = Some(
569 srv.mock("POST", "/api/transactions/commit")
570 .match_header(HEADER_X_APP_NAME, AUTH_PROVIDER)
571 .match_header("authorization", format!("Bearer {}", TOKEN.as_str()).as_str())
572 .with_status(202)
573 .expect(1)
574 .with_header("content-type", "application/json")
575 .create(),
576 );
577 }
578 Err(crate::Error::Wallet(WalletError::InvalidTransaction(_))) => {
579 let mock_user_repo = example_get_user(SwapPaymentDetailKey::Iota, false, 1, KycType::Undefined);
580 sdk.repo = Some(Box::new(mock_user_repo));
581
582 let mock_wallet_manager = example_wallet_borrow();
583 sdk.active_user = Some(crate::types::users::ActiveUser {
584 username: USERNAME.into(),
585 wallet_manager: Box::new(mock_wallet_manager),
586 });
587
588 sdk.access_token = Some(TOKEN.clone());
589
590 let mock_tx_response = GetTransactionDetailsResponse {
591 system_address: "".to_string(),
592 amount: dec!(5.0).into(),
593 status: ApiTxStatus::Invalid(vec!["ReceiverNotVerified".to_string()]),
594 network: example_api_network(IOTA_NETWORK_KEY.to_string()),
595 };
596 let body = serde_json::to_string(&mock_tx_response).unwrap();
597
598 mock_server_details = Some(
599 srv.mock("GET", "/api/transactions/details?index=123")
600 .match_header(HEADER_X_APP_NAME, AUTH_PROVIDER)
601 .match_header("authorization", format!("Bearer {}", TOKEN.as_str()).as_str())
602 .with_status(200)
603 .with_body(&body)
604 .with_header("content-type", "application/json")
605 .create(),
606 );
607 }
608 Err(error) => {
609 handle_error_test_cases(error, &mut sdk, 1, 1).await;
610 }
611 }
612
613 let pin = EncryptionPin::try_from_string("123456").unwrap();
615 let response = sdk.confirm_purchase_request(&pin, PURCHASE_ID).await;
616
617 match expected {
619 Ok(_) => response.unwrap(),
620 Err(ref err) => {
621 assert_eq!(response.unwrap_err().to_string(), err.to_string());
622 }
623 }
624 if mock_server_details.is_some() & mock_server_commit.is_some() {
625 mock_server_details.unwrap().assert();
626 mock_server_commit.unwrap().assert();
627 }
628 }
629
630 #[rstest]
631 #[case::success(Ok(example_tx_details()))]
632 #[case::user_init_error(Err(crate::Error::UserNotInitialized))]
633 #[case::unauthorized(Err(crate::Error::MissingAccessToken))]
634 #[case::missing_config(Err(crate::Error::MissingConfig))]
635 #[tokio::test]
636 async fn test_get_purchase_details(#[case] expected: Result<GetTransactionDetailsResponse>) {
637 let (mut srv, config, _cleanup) = set_config().await;
639 let mut sdk = Sdk::new(config).unwrap();
640 let mut mock_server = None;
641
642 match &expected {
643 Ok(_) => {
644 sdk.repo = Some(Box::new(MockUserRepo::new()));
645 sdk.active_user = Some(crate::types::users::ActiveUser {
646 username: USERNAME.into(),
647 wallet_manager: Box::new(MockWalletManager::new()),
648 });
649 sdk.access_token = Some(TOKEN.clone());
650
651 let mock_response = example_tx_details();
652 let body = serde_json::to_string(&mock_response).unwrap();
653
654 mock_server = Some(
655 srv.mock("GET", "/api/transactions/details?index=123")
656 .match_header(HEADER_X_APP_NAME, AUTH_PROVIDER)
657 .match_header("authorization", format!("Bearer {}", TOKEN.as_str()).as_str())
658 .with_status(200)
659 .with_body(&body)
660 .with_header("content-type", "application/json")
661 .with_body(&body)
662 .create(),
663 );
664 }
665 Err(error) => {
666 handle_error_test_cases(error, &mut sdk, 0, 0).await;
667 }
668 }
669
670 let response = sdk.get_purchase_details(PURCHASE_ID).await;
672
673 match expected {
675 Ok(resp) => {
676 assert_eq!(
677 GetTransactionDetailsResponse {
678 system_address: response.as_ref().unwrap().system_address.clone(),
679 amount: response.as_ref().unwrap().amount.into(),
680 status: response.unwrap().status,
681 network: example_api_network(IOTA_NETWORK_KEY.to_string()),
682 },
683 resp
684 );
685 }
686 Err(ref expected_err) => {
687 assert_eq!(response.err().unwrap().to_string(), expected_err.to_string());
688 }
689 }
690 if let Some(m) = mock_server {
691 m.assert();
692 }
693 }
694
695 #[rstest]
696 #[case::success(Ok(()))]
697 #[case::repo_init_error(Err(crate::Error::UserRepoNotInitialized))]
698 #[case::user_init_error(Err(crate::Error::UserNotInitialized))]
699 #[case::missing_config(Err(crate::Error::MissingConfig))]
700 #[tokio::test]
701 async fn test_send_amount(#[case] expected: Result<()>) {
702 let (_srv, config, _cleanup) = set_config().await;
704 let mut sdk = Sdk::new(config).unwrap();
705 sdk.set_networks(example_api_networks());
706 sdk.set_network(IOTA_NETWORK_KEY.to_string()).await.unwrap();
707
708 match &expected {
709 Ok(_) => {
710 let mock_user_repo = example_get_user(SwapPaymentDetailKey::Iota, false, 1, KycType::Undefined);
711 sdk.repo = Some(Box::new(mock_user_repo));
712
713 let mut mock_wallet_manager = MockWalletManager::new();
714 mock_wallet_manager.expect_try_get().returning(move |_, _, _, _, _| {
715 let mut mock_wallet = MockWalletUser::new();
716 mock_wallet
717 .expect_send_amount()
718 .times(1)
719 .returning(move |_| Ok(String::from("transaction id")));
720 Ok(WalletBorrow::from(mock_wallet))
721 });
722
723 sdk.active_user = Some(crate::types::users::ActiveUser {
724 username: USERNAME.into(),
725 wallet_manager: Box::new(mock_wallet_manager),
726 });
727 }
728 Err(error) => {
729 handle_error_test_cases(error, &mut sdk, 1, 0).await;
730 }
731 }
732
733 let amount = CryptoAmount::try_from(dec!(25.0)).unwrap();
735 let response = sdk
736 .send_amount(
737 &EncryptionPin::try_from_string("123456").unwrap(),
738 "smrq1...",
739 amount,
740 Some(String::from("test message").into_bytes()),
741 )
742 .await;
743
744 match expected {
746 Ok(_) => {
747 response.unwrap();
748 }
749 Err(ref expected_err) => {
750 assert_eq!(response.err().unwrap().to_string(), expected_err.to_string());
751 }
752 }
753 }
754
755 #[tokio::test]
756 async fn test_send_amount_with_eth_should_trigger_a_call_to_set_wallet_transaction() {
757 let (_srv, config, _cleanup) = set_config().await;
759 let mut sdk = Sdk::new(config).unwrap();
760 sdk.set_networks(example_api_networks());
761 sdk.set_network(ETH_NETWORK_KEY.to_string()).await.unwrap();
762
763 let wallet_transaction = WalletTxInfo {
764 date: String::new(),
765 block_id: Some(String::new()),
766 transaction_id: String::from("tx_id"),
767 receiver: String::new(),
768 incoming: false,
769 amount: 5.0,
770 network_key: ETH_NETWORK_KEY.to_string(),
771 status: format!("{:?}", InclusionState::Pending),
772 explorer_url: Some(String::new()),
773 };
774
775 let wallet_transactions = vec![wallet_transaction.clone()].to_owned();
776
777 let mut mock_user_repo = example_get_user(SwapPaymentDetailKey::Eth, false, 2, KycType::Undefined);
778 mock_user_repo
779 .expect_set_wallet_transactions()
780 .times(1)
781 .returning(move |_, expected_wallet_transactions| {
782 assert_eq!(wallet_transactions, expected_wallet_transactions);
783 Ok(())
784 });
785 sdk.repo = Some(Box::new(mock_user_repo));
786
787 let mut mock_wallet_manager = MockWalletManager::new();
788 mock_wallet_manager.expect_try_get().returning(move |_, _, _, _, _| {
789 let mut mock_wallet = MockWalletUser::new();
790 mock_wallet
791 .expect_send_amount()
792 .times(1)
793 .returning(move |_| Ok(String::from("tx_id")));
794
795 let value = wallet_transaction.clone();
796 mock_wallet
797 .expect_get_wallet_tx()
798 .times(1)
799 .returning(move |_| Ok(value.clone()));
800
801 Ok(WalletBorrow::from(mock_wallet))
802 });
803
804 sdk.active_user = Some(crate::types::users::ActiveUser {
805 username: USERNAME.into(),
806 wallet_manager: Box::new(mock_wallet_manager),
807 });
808
809 let amount = CryptoAmount::try_from(dec!(5.0)).unwrap();
811 let response = sdk
812 .send_amount(
813 &EncryptionPin::try_from_string("123456").unwrap(),
814 "0xb0b...",
815 amount,
816 Some(String::from("test message").into_bytes()),
817 )
818 .await;
819
820 response.unwrap();
822 }
823
824 #[rstest]
825 #[case::success(Ok(examples_wallet_tx_list()))]
826 #[case::unauthorized(Err(crate::Error::MissingAccessToken))]
827 #[case::missing_config(Err(crate::Error::MissingConfig))]
828 #[tokio::test]
829 async fn test_get_tx_list(#[case] expected: Result<GetTxsDetailsResponse>) {
830 let (mut srv, config, _cleanup) = set_config().await;
832 let mut sdk = Sdk::new(config).unwrap();
833
834 let start = 1u32;
835 let limit = 5u32;
836
837 let mut mock_server = None;
838 match &expected {
839 Ok(_) => {
840 let mock_user_repo = example_get_user(SwapPaymentDetailKey::Iota, false, 1, KycType::Undefined);
841 sdk.repo = Some(Box::new(mock_user_repo));
842 sdk.active_user = Some(crate::types::users::ActiveUser {
843 username: USERNAME.into(),
844 wallet_manager: Box::new(MockWalletManager::new()),
845 });
846 sdk.access_token = Some(TOKEN.clone());
847
848 let txs_details_mock_response = examples_wallet_tx_list();
849 let body = serde_json::to_string(&txs_details_mock_response).unwrap();
850
851 mock_server = Some(
852 srv.mock("GET", "/api/transactions/txs-details")
853 .match_header(HEADER_X_APP_NAME, AUTH_PROVIDER)
854 .match_header("authorization", format!("Bearer {}", TOKEN.as_str()).as_str())
855 .match_query(Matcher::Exact(format!("is_sender=false&start={start}&limit={limit}")))
856 .with_status(200)
857 .with_body(&body)
858 .expect(1)
859 .with_header("content-type", "application/json")
860 .with_body(&body)
861 .create(),
862 );
863 }
864 Err(error) => {
865 handle_error_test_cases(error, &mut sdk, 0, 1).await;
866 }
867 }
868
869 let response = sdk.get_tx_list(start, limit).await;
871
872 match expected {
874 Ok(_) => assert!(response.is_ok()),
875 Err(ref err) => {
876 assert_eq!(response.unwrap_err().to_string(), err.to_string());
877 }
878 }
879 if let Some(m) = mock_server {
880 m.assert();
881 }
882 }
883}