1use std::time::Duration;
8
9use base64ct::Encoding;
10use cynic::{MutationBuilder, QueryBuilder};
11use futures::Stream;
12use iota_transaction_builder::WaitForTransaction;
13use iota_types::{
14 Address, SenderSignedTransaction, SignedTransaction, Transaction, TransactionDigest,
15 TransactionEffects, UserSignature,
16};
17
18use crate::{
19 GraphQLClient, TransactionDataEffects,
20 api::define_query,
21 error::{GraphQLError, GraphQLResult},
22 pagination::{Page, PaginationFilter, PaginationFilterResponse},
23 query_types::{
24 AddressTransactionBlocksQueryFragment, AddressTransactionRelationship,
25 AddressTransactionsQueryArgs, AddressTransactionsQueryFragment, ExecuteTransactionArgs,
26 ExecuteTransactionQueryFragment, TransactionBlockArgs,
27 TransactionBlockCheckpointQueryFragment, TransactionBlockEffectsQueryFragment,
28 TransactionBlockIndexedQueryFragment, TransactionBlockQueryFragment,
29 TransactionBlockWithEffectsQueryFragment, TransactionBlocksEffectsQueryFragment,
30 TransactionBlocksQueryArgs, TransactionBlocksQueryFragment,
31 TransactionBlocksWithEffectsQueryFragment, TransactionsFilter,
32 },
33 streams::stream_paginated_query,
34};
35
36define_query! {
37 #[derive(Clone)]
40 pub struct ListTransactionsQuery {
41 client: GraphQLClient,
42 filter: Option<TransactionsFilter>,
43 pagination: PaginationFilter,
44 }
45 output: GraphQLResult<Page<SignedTransaction>>;
46}
47
48impl ListTransactionsQuery {
49 pub fn filter(mut self, filter: TransactionsFilter) -> Self {
51 self.filter = Some(filter);
52 self
53 }
54
55 pub fn pagination(mut self, pagination: PaginationFilter) -> Self {
57 self.pagination = pagination;
58 self
59 }
60
61 pub fn stream(self) -> impl Stream<Item = GraphQLResult<SignedTransaction>> + Unpin {
64 let pagination = self.pagination.clone();
65 stream_paginated_query(move |page| self.clone().pagination(page).send(), pagination)
66 }
67
68 fn operation(
69 filter: Option<TransactionsFilter>,
70 pagination: PaginationFilterResponse,
71 ) -> cynic::Operation<TransactionBlocksQueryFragment, TransactionBlocksQueryArgs> {
72 TransactionBlocksQueryFragment::build(TransactionBlocksQueryArgs {
73 after: pagination.after,
74 before: pagination.before,
75 filter: filter.map(Into::into),
76 first: pagination.first,
77 last: pagination.last,
78 })
79 }
80
81 async fn send(self) -> GraphQLResult<Page<SignedTransaction>> {
82 let Self {
83 client,
84 pagination,
85 filter,
86 } = self;
87 let pagination = client.pagination_filter(pagination).await;
88 let response = client
89 .run_query(&Self::operation(filter, pagination))
90 .await?;
91
92 let txc = response.transaction_blocks;
93 let page_info = txc.page_info;
94
95 let transactions = txc
96 .nodes
97 .into_iter()
98 .map(|n| n.try_into())
99 .collect::<GraphQLResult<Vec<_>>>()?;
100 Ok(Page::new(page_info, transactions))
101 }
102}
103
104define_query! {
105 #[derive(Clone)]
108 pub struct ListAddressTransactionsQuery {
109 client: GraphQLClient,
110 address: Address,
111 relation: Option<AddressTransactionRelationship>,
112 filter: Option<TransactionsFilter>,
113 pagination: PaginationFilter,
114 }
115 output: GraphQLResult<Page<SignedTransaction>>;
116}
117
118impl ListAddressTransactionsQuery {
119 pub fn relation(mut self, relation: AddressTransactionRelationship) -> Self {
122 self.relation = Some(relation);
123 self
124 }
125
126 pub fn filter(mut self, filter: TransactionsFilter) -> Self {
128 self.filter = Some(filter);
129 self
130 }
131
132 pub fn pagination(mut self, pagination: PaginationFilter) -> Self {
134 self.pagination = pagination;
135 self
136 }
137
138 pub fn stream(self) -> impl Stream<Item = GraphQLResult<SignedTransaction>> + Unpin {
141 let pagination = self.pagination.clone();
142 stream_paginated_query(move |page| self.clone().pagination(page).send(), pagination)
143 }
144
145 fn operation(
146 address: Address,
147 relation: Option<AddressTransactionRelationship>,
148 filter: Option<TransactionsFilter>,
149 pagination: PaginationFilterResponse,
150 ) -> cynic::Operation<AddressTransactionsQueryFragment, AddressTransactionsQueryArgs> {
151 AddressTransactionsQueryFragment::build(AddressTransactionsQueryArgs {
152 address,
153 relation,
154 after: pagination.after,
155 before: pagination.before,
156 filter: filter.map(Into::into),
157 first: pagination.first,
158 last: pagination.last,
159 })
160 }
161
162 async fn send(self) -> GraphQLResult<Page<SignedTransaction>> {
163 let Self {
164 client,
165 pagination,
166 address,
167 relation,
168 filter,
169 } = self;
170 let pagination = client.pagination_filter(pagination).await;
171 let response = client
172 .run_query(&Self::operation(address, relation, filter, pagination))
173 .await?;
174
175 let Some(AddressTransactionBlocksQueryFragment { transaction_blocks }) = response.address
176 else {
177 return Ok(Page::new_empty());
178 };
179
180 let transactions = transaction_blocks
181 .nodes
182 .into_iter()
183 .map(|n| n.try_into())
184 .collect::<GraphQLResult<Vec<_>>>()?;
185
186 Ok(Page::new(transaction_blocks.page_info, transactions))
187 }
188}
189
190define_query! {
191 #[derive(Clone)]
194 pub struct ListTransactionsEffectsQuery {
195 client: GraphQLClient,
196 filter: Option<TransactionsFilter>,
197 pagination: PaginationFilter,
198 }
199 output: GraphQLResult<Page<TransactionEffects>>;
200}
201
202impl ListTransactionsEffectsQuery {
203 pub fn filter(mut self, filter: TransactionsFilter) -> Self {
205 self.filter = Some(filter);
206 self
207 }
208
209 pub fn pagination(mut self, pagination: PaginationFilter) -> Self {
211 self.pagination = pagination;
212 self
213 }
214
215 pub fn stream(self) -> impl Stream<Item = GraphQLResult<TransactionEffects>> + Unpin {
218 let pagination = self.pagination.clone();
219 stream_paginated_query(move |page| self.clone().pagination(page).send(), pagination)
220 }
221
222 fn operation(
223 filter: Option<TransactionsFilter>,
224 pagination: PaginationFilterResponse,
225 ) -> cynic::Operation<TransactionBlocksEffectsQueryFragment, TransactionBlocksQueryArgs> {
226 TransactionBlocksEffectsQueryFragment::build(TransactionBlocksQueryArgs {
227 after: pagination.after,
228 before: pagination.before,
229 filter: filter.map(Into::into),
230 first: pagination.first,
231 last: pagination.last,
232 })
233 }
234
235 async fn send(self) -> GraphQLResult<Page<TransactionEffects>> {
236 let Self {
237 client,
238 pagination,
239 filter,
240 } = self;
241 let pagination = client.pagination_filter(pagination).await;
242 let response = client
243 .run_query(&Self::operation(filter, pagination))
244 .await?;
245
246 let txc = response.transaction_blocks;
247 let page_info = txc.page_info;
248
249 let transactions = txc
250 .nodes
251 .into_iter()
252 .map(|n| n.try_into())
253 .collect::<GraphQLResult<Vec<_>>>()?;
254 Ok(Page::new(page_info, transactions))
255 }
256}
257
258define_query! {
259 #[derive(Clone)]
262 pub struct ListTransactionsDataEffectsQuery {
263 client: GraphQLClient,
264 filter: Option<TransactionsFilter>,
265 pagination: PaginationFilter,
266 }
267 output: GraphQLResult<Page<TransactionDataEffects>>;
268}
269
270impl ListTransactionsDataEffectsQuery {
271 pub fn filter(mut self, filter: TransactionsFilter) -> Self {
273 self.filter = Some(filter);
274 self
275 }
276
277 pub fn pagination(mut self, pagination: PaginationFilter) -> Self {
279 self.pagination = pagination;
280 self
281 }
282
283 pub fn stream(self) -> impl Stream<Item = GraphQLResult<TransactionDataEffects>> + Unpin {
286 let pagination = self.pagination.clone();
287 stream_paginated_query(move |page| self.clone().pagination(page).send(), pagination)
288 }
289
290 fn operation(
291 filter: Option<TransactionsFilter>,
292 pagination: PaginationFilterResponse,
293 ) -> cynic::Operation<TransactionBlocksWithEffectsQueryFragment, TransactionBlocksQueryArgs>
294 {
295 TransactionBlocksWithEffectsQueryFragment::build(TransactionBlocksQueryArgs {
296 after: pagination.after,
297 before: pagination.before,
298 filter: filter.map(Into::into),
299 first: pagination.first,
300 last: pagination.last,
301 })
302 }
303
304 async fn send(self) -> GraphQLResult<Page<TransactionDataEffects>> {
305 let Self {
306 client,
307 pagination,
308 filter,
309 } = self;
310 let pagination = client.pagination_filter(pagination).await;
311 let response = client
312 .run_query(&Self::operation(filter, pagination))
313 .await?;
314
315 let txc = response.transaction_blocks;
316 let page_info = txc.page_info;
317
318 let transactions = {
319 txc.nodes
320 .into_iter()
321 .map(|node| {
322 let (Some(bcs), Some(effects)) = (node.bcs, node.effects) else {
323 return Err(GraphQLError::EmptyResponseField(
324 "transaction bcs or effects",
325 ));
326 };
327 let bcs = crate::base64::decode(bcs.0.as_str())?;
328 let effects = crate::base64::decode(effects.bcs.as_ref().unwrap().0.as_str())?;
329 let transaction: SenderSignedTransaction =
330 bcs::from_bytes(&bcs).map_err(iota_types::BcsError::new)?;
331 let effects: TransactionEffects =
332 bcs::from_bytes(&effects).map_err(iota_types::BcsError::new)?;
333
334 Ok(TransactionDataEffects {
335 signed_transaction: transaction.into(),
336 effects,
337 })
338 })
339 .collect::<GraphQLResult<Vec<_>>>()?
340 };
341
342 Ok(Page::new(page_info, transactions))
343 }
344}
345
346define_query! {
347 pub struct ExecuteTransactionQuery {
350 client: GraphQLClient,
351 signatures: Vec<String>,
352 transaction: Transaction,
353 wait_for: Option<WaitForTransaction>,
354 }
355 output: GraphQLResult<TransactionEffects>;
356}
357
358impl ExecuteTransactionQuery {
359 pub fn wait_for(mut self, wait_for: WaitForTransaction) -> Self {
362 self.wait_for = Some(wait_for);
363 self
364 }
365
366 async fn send(self) -> GraphQLResult<TransactionEffects> {
367 let operation = ExecuteTransactionQueryFragment::build(ExecuteTransactionArgs {
368 signatures: self.signatures,
369 tx_bytes: base64ct::Base64::encode_string(
370 bcs::to_bytes(&self.transaction).unwrap().as_ref(),
371 ),
372 });
373
374 let response = self.client.run_query(&operation).await?;
375
376 let result = response.execute_transaction_block;
377 let bcs = crate::base64::decode(result.effects.bcs.0.as_str())?;
378 let effects: TransactionEffects =
379 bcs::from_bytes(&bcs).map_err(iota_types::BcsError::new)?;
380
381 if let Some(wait_for) = self.wait_for {
382 self.client
383 .wait_for_transaction(self.transaction.digest(), wait_for)
384 .await?;
385 }
386
387 Ok(effects)
388 }
389}
390
391define_query! {
392 pub struct WaitForTransactionQuery {
395 client: GraphQLClient,
396 digest: TransactionDigest,
397 wait_for: WaitForTransaction,
398 timeout: Option<Duration>,
399 }
400 output: GraphQLResult<()>;
401}
402
403impl WaitForTransactionQuery {
404 pub fn timeout(mut self, timeout: Duration) -> Self {
406 self.timeout = Some(timeout);
407 self
408 }
409
410 async fn send(self) -> GraphQLResult<()> {
411 let client = &self.client;
412 let digest = self.digest;
413 crate::wait::timeout(
414 self.timeout.unwrap_or_else(|| Duration::from_secs(60)),
415 async {
416 loop {
417 if match self.wait_for {
418 WaitForTransaction::IndexedOnNode => client.is_transaction_indexed_on_node(digest).await?,
419 WaitForTransaction::Finalized => client.is_transaction_finalized(digest).await?,
420 _ => unimplemented!(
421 "a new WaitForTransaction enum variant was added and needs to be handled"
422 ),
423 } {
424 break Ok(());
425 }
426 crate::wait::sleep(Duration::from_millis(100)).await;
427 }
428 },
429 )
430 .await
431 .map_err(|_| GraphQLError::Timeout)?
432 }
433}
434
435define_query! {
436 pub struct GetTransactionQuery {
438 client: GraphQLClient,
439 digest: TransactionDigest,
440 }
441 output: GraphQLResult<Option<SignedTransaction>>;
442}
443
444impl GetTransactionQuery {
445 async fn send(self) -> GraphQLResult<Option<SignedTransaction>> {
446 let operation = TransactionBlockQueryFragment::build(TransactionBlockArgs {
447 digest: self.digest.to_string(),
448 });
449 let response = self.client.run_query(&operation).await?;
450
451 response
452 .transaction_block
453 .map(TryInto::try_into)
454 .transpose()
455 }
456}
457
458define_query! {
459 pub struct GetTransactionEffectsQuery {
462 client: GraphQLClient,
463 digest: TransactionDigest,
464 }
465 output: GraphQLResult<Option<TransactionEffects>>;
466}
467
468impl GetTransactionEffectsQuery {
469 async fn send(self) -> GraphQLResult<Option<TransactionEffects>> {
470 let operation = TransactionBlockEffectsQueryFragment::build(TransactionBlockArgs {
471 digest: self.digest.to_string(),
472 });
473 let response = self.client.run_query(&operation).await?;
474
475 response
476 .transaction_block
477 .map(TryInto::try_into)
478 .transpose()
479 }
480}
481
482define_query! {
483 pub struct GetTransactionDataEffectsQuery {
486 client: GraphQLClient,
487 digest: TransactionDigest,
488 }
489 output: GraphQLResult<Option<TransactionDataEffects>>;
490}
491
492impl GetTransactionDataEffectsQuery {
493 async fn send(self) -> GraphQLResult<Option<TransactionDataEffects>> {
494 let operation = TransactionBlockWithEffectsQueryFragment::build(TransactionBlockArgs {
495 digest: self.digest.to_string(),
496 });
497 let response = self.client.run_query(&operation).await?;
498
499 match response.transaction_block.map(|tx| (tx.bcs, tx.effects)) {
500 Some((Some(bcs), Some(effects))) => {
501 let bcs = crate::base64::decode(bcs.0.as_str())?;
502 let effects = crate::base64::decode(effects.bcs.unwrap().0.as_str())?;
503 let transaction: SenderSignedTransaction =
504 bcs::from_bytes(&bcs).map_err(iota_types::BcsError::new)?;
505 let effects: TransactionEffects =
506 bcs::from_bytes(&effects).map_err(iota_types::BcsError::new)?;
507
508 Ok(Some(TransactionDataEffects {
509 signed_transaction: transaction.into(),
510 effects,
511 }))
512 }
513 _ => Ok(None),
514 }
515 }
516}
517
518define_query! {
519 pub struct IsTransactionIndexedOnNodeQuery {
522 client: GraphQLClient,
523 digest: TransactionDigest,
524 }
525 output: GraphQLResult<bool>;
526}
527
528impl IsTransactionIndexedOnNodeQuery {
529 async fn send(self) -> GraphQLResult<bool> {
530 let operation = TransactionBlockIndexedQueryFragment::build(TransactionBlockArgs {
531 digest: self.digest.to_string(),
532 });
533 Ok(self
534 .client
535 .run_query(&operation)
536 .await?
537 .is_transaction_indexed_on_node)
538 }
539}
540
541define_query! {
542 pub struct IsTransactionFinalizedQuery {
545 client: GraphQLClient,
546 digest: TransactionDigest,
547 }
548 output: GraphQLResult<bool>;
549}
550
551impl IsTransactionFinalizedQuery {
552 async fn send(self) -> GraphQLResult<bool> {
553 let operation = TransactionBlockCheckpointQueryFragment::build(TransactionBlockArgs {
554 digest: self.digest.to_string(),
555 });
556 let response = self.client.run_query(&operation).await?;
557 if let Some(block) = response.transaction_block
558 && block
559 .effects
560 .as_ref()
561 .and_then(|e| e.checkpoint.as_ref())
562 .is_some()
563 {
564 return Ok(true);
565 }
566 Ok(false)
567 }
568}
569
570impl GraphQLClient {
571 pub fn transaction(&self, digest: TransactionDigest) -> GetTransactionQuery {
573 GetTransactionQuery {
574 client: self.clone(),
575 digest,
576 }
577 }
578
579 pub fn transactions(&self) -> ListTransactionsQuery {
581 ListTransactionsQuery {
582 client: self.clone(),
583 filter: None,
584 pagination: PaginationFilter::default(),
585 }
586 }
587
588 pub fn address_transactions(&self, address: Address) -> ListAddressTransactionsQuery {
590 ListAddressTransactionsQuery {
591 client: self.clone(),
592 address,
593 relation: None,
594 filter: None,
595 pagination: PaginationFilter::default(),
596 }
597 }
598
599 pub fn transaction_effects(&self, digest: TransactionDigest) -> GetTransactionEffectsQuery {
601 GetTransactionEffectsQuery {
602 client: self.clone(),
603 digest,
604 }
605 }
606
607 pub fn transactions_effects(&self) -> ListTransactionsEffectsQuery {
609 ListTransactionsEffectsQuery {
610 client: self.clone(),
611 filter: None,
612 pagination: PaginationFilter::default(),
613 }
614 }
615
616 pub fn transaction_data_effects(
618 &self,
619 digest: TransactionDigest,
620 ) -> GetTransactionDataEffectsQuery {
621 GetTransactionDataEffectsQuery {
622 client: self.clone(),
623 digest,
624 }
625 }
626
627 pub fn transactions_data_effects(&self) -> ListTransactionsDataEffectsQuery {
629 ListTransactionsDataEffectsQuery {
630 client: self.clone(),
631 filter: None,
632 pagination: PaginationFilter::default(),
633 }
634 }
635
636 pub fn execute_transaction(
638 &self,
639 signatures: &[UserSignature],
640 transaction: &Transaction,
641 ) -> ExecuteTransactionQuery {
642 ExecuteTransactionQuery {
643 client: self.clone(),
644 signatures: signatures.iter().map(|s| s.to_base64()).collect(),
645 transaction: transaction.clone(),
646 wait_for: None,
647 }
648 }
649
650 pub fn is_transaction_indexed_on_node(
655 &self,
656 digest: TransactionDigest,
657 ) -> IsTransactionIndexedOnNodeQuery {
658 IsTransactionIndexedOnNodeQuery {
659 client: self.clone(),
660 digest,
661 }
662 }
663
664 pub fn is_transaction_finalized(
667 &self,
668 digest: TransactionDigest,
669 ) -> IsTransactionFinalizedQuery {
670 IsTransactionFinalizedQuery {
671 client: self.clone(),
672 digest,
673 }
674 }
675
676 pub fn wait_for_transaction(
680 &self,
681 digest: TransactionDigest,
682 wait_for: WaitForTransaction,
683 ) -> WaitForTransactionQuery {
684 WaitForTransactionQuery {
685 client: self.clone(),
686 digest,
687 wait_for,
688 timeout: None,
689 }
690 }
691}
692
693#[cfg(all(test, not(target_arch = "wasm32")))]
694mod tests {
695 use std::time::Duration;
696
697 use base64ct::Encoding;
698 use iota_types::{Address, Ed25519PublicKey, Ed25519Signature, SimpleSignature, UserSignature};
699
700 use crate::{
701 GraphQLClient, GraphQLError, WaitForTransaction,
702 query_types::{AddressTransactionRelationship, TransactionsFilter},
703 test_utils::{
704 assert_backward_page, assert_forward_page, backward_page, forward_page, sent_variables,
705 test_client, test_transaction,
706 },
707 };
708
709 #[tokio::test]
710 async fn digest_getters_send_the_digest() {
711 let digest = test_transaction().digest();
712 let expected = digest.to_string();
713 let vars = sent_variables("TransactionBlockQueryFragment", |client| async move {
714 let _ = client.transaction(digest).await;
715 })
716 .await;
717 assert_eq!(vars["digest"], expected);
718
719 let vars = sent_variables(
720 "TransactionBlockEffectsQueryFragment",
721 |client| async move {
722 let _ = client.transaction_effects(digest).await;
723 },
724 )
725 .await;
726 assert_eq!(vars["digest"], expected);
727
728 let vars = sent_variables(
729 "TransactionBlockWithEffectsQueryFragment",
730 |client| async move {
731 let _ = client.transaction_data_effects(digest).await;
732 },
733 )
734 .await;
735 assert_eq!(vars["digest"], expected);
736
737 let vars = sent_variables(
738 "TransactionBlockIndexedQueryFragment",
739 |client| async move {
740 let _ = client.is_transaction_indexed_on_node(digest).await;
741 },
742 )
743 .await;
744 assert_eq!(vars["digest"], expected);
745
746 let vars = sent_variables(
747 "TransactionBlockCheckpointQueryFragment",
748 |client| async move {
749 let _ = client.is_transaction_finalized(digest).await;
750 },
751 )
752 .await;
753 assert_eq!(vars["digest"], expected);
754 }
755
756 #[tokio::test]
757 async fn execute_transaction_sends_the_signatures_and_transaction() {
758 let transaction = test_transaction();
759 let signature = UserSignature::Simple(SimpleSignature::Ed25519 {
760 signature: Ed25519Signature::new([1; 64]),
761 public_key: Ed25519PublicKey::new([2; 32]),
762 });
763 let expected_signature = signature.to_base64();
764 let expected_tx_bytes =
765 base64ct::Base64::encode_string(&bcs::to_bytes(&transaction).unwrap());
766 let vars = sent_variables("ExecuteTransactionQueryFragment", |client| async move {
767 let _ = client.execute_transaction(&[signature], &transaction).await;
768 })
769 .await;
770 assert_eq!(vars["signatures"], serde_json::json!([expected_signature]));
771 assert_eq!(vars["txBytes"], expected_tx_bytes);
772 }
773
774 #[tokio::test]
775 async fn wait_for_transaction_sends_the_digest_to_the_status_query() {
776 let digest = test_transaction().digest();
777 for (wait_for, operation) in [
778 (
779 WaitForTransaction::IndexedOnNode,
780 "TransactionBlockIndexedQueryFragment",
781 ),
782 (
783 WaitForTransaction::Finalized,
784 "TransactionBlockCheckpointQueryFragment",
785 ),
786 ] {
787 let vars = sent_variables(operation, |client| async move {
788 let _ = client.wait_for_transaction(digest, wait_for).await;
789 })
790 .await;
791 assert_eq!(vars["digest"], digest.to_string());
792 }
793 }
794
795 #[tokio::test]
796 async fn wait_for_transaction_stops_at_the_timeout() {
797 let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
798 let client =
799 GraphQLClient::new(&format!("http://{}", listener.local_addr().unwrap())).unwrap();
800 let result = tokio::time::timeout(
801 Duration::from_secs(10),
802 client
803 .wait_for_transaction(test_transaction().digest(), WaitForTransaction::Finalized)
804 .timeout(Duration::from_millis(100)),
805 )
806 .await
807 .expect("the query's own timeout fires first");
808 assert!(matches!(result, Err(GraphQLError::Timeout)));
809 }
810
811 fn sent_by_framework() -> TransactionsFilter {
812 TransactionsFilter::default().with_sent_address(Address::FRAMEWORK)
813 }
814
815 #[tokio::test]
816 async fn transactions_sends_the_filter_and_pagination() {
817 let vars = sent_variables("TransactionBlocksQueryFragment", |client| async move {
818 let _ = client
819 .transactions()
820 .filter(sent_by_framework())
821 .pagination(backward_page())
822 .await;
823 })
824 .await;
825 assert_eq!(
826 vars["filter"]["sentAddress"],
827 Address::FRAMEWORK.to_string()
828 );
829 assert_backward_page(&vars);
830
831 let vars = sent_variables("TransactionBlocksQueryFragment", |client| async move {
832 let _ = client
833 .transactions()
834 .filter(sent_by_framework())
835 .pagination(forward_page())
836 .await;
837 })
838 .await;
839 assert_forward_page(&vars);
840 }
841
842 #[tokio::test]
843 async fn transactions_effects_sends_the_filter_and_pagination() {
844 let vars = sent_variables(
845 "TransactionBlocksEffectsQueryFragment",
846 |client| async move {
847 let _ = client
848 .transactions_effects()
849 .filter(sent_by_framework())
850 .pagination(backward_page())
851 .await;
852 },
853 )
854 .await;
855 assert_eq!(
856 vars["filter"]["sentAddress"],
857 Address::FRAMEWORK.to_string()
858 );
859 assert_backward_page(&vars);
860
861 let vars = sent_variables(
862 "TransactionBlocksEffectsQueryFragment",
863 |client| async move {
864 let _ = client
865 .transactions_effects()
866 .filter(sent_by_framework())
867 .pagination(forward_page())
868 .await;
869 },
870 )
871 .await;
872 assert_forward_page(&vars);
873 }
874
875 #[tokio::test]
876 async fn transactions_data_effects_sends_the_filter_and_pagination() {
877 let vars = sent_variables(
878 "TransactionBlocksWithEffectsQueryFragment",
879 |client| async move {
880 let _ = client
881 .transactions_data_effects()
882 .filter(sent_by_framework())
883 .pagination(backward_page())
884 .await;
885 },
886 )
887 .await;
888 assert_eq!(
889 vars["filter"]["sentAddress"],
890 Address::FRAMEWORK.to_string()
891 );
892 assert_backward_page(&vars);
893
894 let vars = sent_variables(
895 "TransactionBlocksWithEffectsQueryFragment",
896 |client| async move {
897 let _ = client
898 .transactions_data_effects()
899 .filter(sent_by_framework())
900 .pagination(forward_page())
901 .await;
902 },
903 )
904 .await;
905 assert_forward_page(&vars);
906 }
907
908 #[tokio::test]
909 async fn address_transactions_sends_the_address_relation_filter_and_pagination() {
910 let vars = sent_variables("AddressTransactionsQueryFragment", |client| async move {
911 let _ = client
912 .address_transactions(Address::STD)
913 .relation(AddressTransactionRelationship::Recv)
914 .filter(sent_by_framework())
915 .pagination(backward_page())
916 .await;
917 })
918 .await;
919 assert_eq!(vars["address"], Address::STD.to_string());
920 assert_eq!(vars["relation"], "RECV");
921 assert_eq!(
922 vars["filter"]["sentAddress"],
923 Address::FRAMEWORK.to_string()
924 );
925 assert_backward_page(&vars);
926
927 let vars = sent_variables("AddressTransactionsQueryFragment", |client| async move {
928 let _ = client
929 .address_transactions(Address::STD)
930 .relation(AddressTransactionRelationship::Recv)
931 .filter(sent_by_framework())
932 .pagination(forward_page())
933 .await;
934 })
935 .await;
936 assert_forward_page(&vars);
937 }
938
939 #[tokio::test]
940 async fn test_transaction_effects_query() {
941 let client = test_client();
942 let transactions = client.transactions().await.unwrap();
943 let tx_digest = transactions.data()[0].transaction.digest();
944 let effects = client.transaction_effects(tx_digest).await.unwrap();
945 assert!(
946 effects.is_some(),
947 "Transaction effects query failed for {} network.",
948 client.rpc_server(),
949 );
950 }
951
952 #[tokio::test]
953 async fn test_transactions_effects_query() {
954 let client = test_client();
955 client
956 .transactions_effects()
957 .await
958 .map_err(|e| {
959 format!(
960 "Transactions effects query failed for {} network: Error {e}",
961 client.rpc_server()
962 )
963 })
964 .unwrap();
965 }
966
967 #[tokio::test]
968 async fn test_transactions_query() {
969 let client = test_client();
970 let transactions = client
971 .transactions()
972 .await
973 .map_err(|e| {
974 format!(
975 "Transactions query failed for {} network: Error {e}",
976 client.rpc_server()
977 )
978 })
979 .unwrap();
980 assert!(
981 !transactions.is_empty(),
982 "Transactions query returned no data for {} network",
983 client.rpc_server()
984 );
985 }
986
987 #[tokio::test]
988 async fn test_address_transactions() {
989 let client = test_client();
990 let transactions = client.transactions().await.unwrap();
991 let sender = transactions.data()[0].transaction.as_v1().sender;
992
993 for relation in [
994 AddressTransactionRelationship::Sent,
995 AddressTransactionRelationship::Recv,
996 AddressTransactionRelationship::Affected,
997 ] {
998 let page = client
999 .address_transactions(sender)
1000 .relation(relation)
1001 .await
1002 .map_err(|e| {
1003 format!(
1004 "Address transactions query with relation {relation:?} failed for {} \
1005 network: Error {e}",
1006 client.rpc_server()
1007 )
1008 })
1009 .unwrap();
1010
1011 if matches!(relation, AddressTransactionRelationship::Sent) {
1012 assert!(
1013 page.data()
1014 .iter()
1015 .all(|tx| tx.transaction.as_v1().sender == sender),
1016 "Sent relation returned a transaction from another sender"
1017 );
1018 }
1019 }
1020 }
1021
1022 #[tokio::test]
1023 async fn test_transaction_data_effects() {
1024 let client = test_client();
1025 let transactions = client.transactions().await.unwrap();
1026 let digest = transactions.data()[0].transaction.digest();
1027
1028 client
1029 .transaction_data_effects(digest)
1030 .await
1031 .unwrap()
1032 .unwrap();
1033 }
1034
1035 #[tokio::test]
1036 async fn test_transactions_data_effects() {
1037 let client = test_client();
1038 let transactions = client.transactions().await.unwrap();
1039 let digest = transactions.data()[0].transaction.digest();
1040
1041 client
1042 .transactions_data_effects()
1043 .filter(TransactionsFilter::default().with_transaction_ids([digest]))
1044 .await
1045 .unwrap();
1046 }
1047}