sui_indexer_alt_jsonrpc/api/transactions/
mod.rs1use futures::future;
5use jsonrpsee::{core::RpcResult, proc_macros::rpc};
6use sui_json_rpc_types::{Page, SuiTransactionBlockResponse, SuiTransactionBlockResponseOptions};
7use sui_open_rpc::Module;
8use sui_open_rpc_macros::open_rpc;
9use sui_types::digests::TransactionDigest;
10
11use self::{error::Error, filter::SuiTransactionBlockResponseQuery};
12
13use crate::{
14 context::Context,
15 error::{InternalContext, RpcError, rpc_bail},
16};
17
18use super::rpc_module::RpcModule;
19
20mod error;
21mod filter;
22mod response;
23
24#[open_rpc(namespace = "sui", tag = "Transactions API")]
25#[rpc(server, namespace = "sui")]
26trait TransactionsApi {
27 #[method(name = "getTransactionBlock")]
29 async fn get_transaction_block(
30 &self,
31 digest: TransactionDigest,
33 options: Option<SuiTransactionBlockResponseOptions>,
35 ) -> RpcResult<SuiTransactionBlockResponse>;
36}
37
38#[open_rpc(namespace = "suix", tag = "Query Transactions API")]
39#[rpc(server, namespace = "suix")]
40trait QueryTransactionsApi {
41 #[method(name = "queryTransactionBlocks")]
53 async fn query_transaction_blocks(
54 &self,
55 query: SuiTransactionBlockResponseQuery,
57 cursor: Option<String>,
59 limit: Option<usize>,
61 descending_order: Option<bool>,
63 ) -> RpcResult<Page<SuiTransactionBlockResponse, String>>;
64}
65
66pub(crate) struct Transactions(pub Context);
67
68pub(crate) struct QueryTransactions(pub Context);
69
70#[async_trait::async_trait]
71impl TransactionsApiServer for Transactions {
72 async fn get_transaction_block(
73 &self,
74 digest: TransactionDigest,
75 options: Option<SuiTransactionBlockResponseOptions>,
76 ) -> RpcResult<SuiTransactionBlockResponse> {
77 let Self(ctx) = self;
78 Ok(
79 response::transaction(ctx, digest, &options.unwrap_or_default())
80 .await
81 .with_internal_context(|| format!("Failed to get transaction {digest}"))?,
82 )
83 }
84}
85
86#[async_trait::async_trait]
87impl QueryTransactionsApiServer for QueryTransactions {
88 async fn query_transaction_blocks(
89 &self,
90 query: SuiTransactionBlockResponseQuery,
91 cursor: Option<String>,
92 limit: Option<usize>,
93 descending_order: Option<bool>,
94 ) -> RpcResult<Page<SuiTransactionBlockResponse, String>> {
95 let Self(ctx) = self;
96
97 let Page {
98 data: digests,
99 next_cursor,
100 has_next_page,
101 } = filter::transactions(ctx, &query.filter, cursor.clone(), limit, descending_order)
102 .await?;
103
104 let options = query.options.unwrap_or_default();
105
106 let tx_futures = digests.iter().map(|d| {
107 async {
108 let mut tx = response::transaction(ctx, *d, &options).await;
109
110 let config = &ctx.config().transactions;
111 let mut interval = tokio::time::interval(std::time::Duration::from_millis(
112 config.tx_retry_interval_ms,
113 ));
114
115 let mut retries = 0;
116 for _ in 0..config.tx_retry_count {
117 if let Err(RpcError::InvalidParams(
120 _e @ (Error::BalanceChangesNotFound(_) | Error::NotFound(_)),
121 )) = tx
122 {
123 interval.tick().await;
124 retries += 1;
125 tx = response::transaction(ctx, *d, &options).await;
126 ctx.metrics()
127 .read_retries
128 .with_label_values(&["tx_response"])
129 .inc();
130 } else {
131 break;
132 }
133 }
134
135 ctx.metrics()
136 .read_retries_per_request
137 .with_label_values(&["tx_response"])
138 .observe(retries as f64);
139 tx
140 }
141 });
142
143 let data = future::join_all(tx_futures)
144 .await
145 .into_iter()
146 .zip(digests)
147 .map(|(r, d)| {
148 if let Err(RpcError::InvalidParams(e @ Error::NotFound(_))) = r {
149 rpc_bail!(e)
150 } else {
151 r.with_internal_context(|| format!("Failed to get transaction {d}"))
152 }
153 })
154 .collect::<Result<Vec<_>, _>>()?;
155
156 Ok(Page {
157 data,
158 next_cursor: next_cursor.or(cursor),
159 has_next_page,
160 })
161 }
162}
163
164impl RpcModule for Transactions {
165 fn schema(&self) -> Module {
166 TransactionsApiOpenRpc::module_doc()
167 }
168
169 fn into_impl(self) -> jsonrpsee::RpcModule<Self> {
170 self.into_rpc()
171 }
172}
173
174impl RpcModule for QueryTransactions {
175 fn schema(&self) -> Module {
176 QueryTransactionsApiOpenRpc::module_doc()
177 }
178
179 fn into_impl(self) -> jsonrpsee::RpcModule<Self> {
180 self.into_rpc()
181 }
182}