hiero_sdk/
transaction_receipt_query.rs1use hiero_sdk_proto::services;
4use hiero_sdk_proto::services::crypto_service_client::CryptoServiceClient;
5use hiero_sdk_proto::services::response::Response;
6use tonic::transport::Channel;
7
8use crate::ledger_id::RefLedgerId;
9use crate::query::{
10 AnyQueryData,
11 QueryExecute,
12 ToQueryProtobuf,
13};
14use crate::{
15 BoxGrpcFuture,
16 Error,
17 Query,
18 Status,
19 ToProtobuf,
20 TransactionId,
21 TransactionReceipt,
22 ValidateChecksums,
23};
24
25pub type TransactionReceiptQuery = Query<TransactionReceiptQueryData>;
31
32#[derive(Default, Clone, Debug)]
33pub struct TransactionReceiptQueryData {
34 transaction_id: Option<TransactionId>,
35 include_children: bool,
36 include_duplicates: bool,
37 validate_status: bool,
38}
39
40impl From<TransactionReceiptQueryData> for AnyQueryData {
41 #[inline]
42 fn from(data: TransactionReceiptQueryData) -> Self {
43 Self::TransactionReceipt(data)
44 }
45}
46
47impl TransactionReceiptQuery {
48 #[must_use]
50 pub fn get_transaction_id(&self) -> Option<TransactionId> {
51 self.data.transaction_id
52 }
53
54 pub fn transaction_id(&mut self, transaction_id: TransactionId) -> &mut Self {
56 self.data.transaction_id = Some(transaction_id);
57 self
58 }
59
60 #[must_use]
63 pub fn get_include_children(&self) -> bool {
64 self.data.include_children
65 }
66
67 pub fn include_children(&mut self, include: bool) -> &mut Self {
70 self.data.include_children = include;
71 self
72 }
73
74 #[must_use]
76 pub fn get_include_duplicates(&self) -> bool {
77 self.data.include_duplicates
78 }
79
80 pub fn include_duplicates(&mut self, include: bool) -> &mut Self {
82 self.data.include_duplicates = include;
83 self
84 }
85
86 #[must_use]
88 pub fn get_validate_status(&self) -> bool {
89 self.data.validate_status
90 }
91
92 pub fn validate_status(&mut self, validate: bool) -> &mut Self {
94 self.data.validate_status = validate;
95 self
96 }
97}
98
99impl ToQueryProtobuf for TransactionReceiptQueryData {
100 fn to_query_protobuf(&self, header: services::QueryHeader) -> services::Query {
101 let transaction_id = self.transaction_id.to_protobuf();
102
103 services::Query {
104 query: Some(services::query::Query::TransactionGetReceipt(
105 services::TransactionGetReceiptQuery {
106 header: Some(header),
107 transaction_id,
108 include_child_receipts: self.include_children,
109 include_duplicates: self.include_duplicates,
110 },
111 )),
112 }
113 }
114}
115
116impl QueryExecute for TransactionReceiptQueryData {
117 type Response = TransactionReceipt;
118
119 fn is_payment_required(&self) -> bool {
120 false
121 }
122
123 fn transaction_id(&self) -> Option<TransactionId> {
124 self.transaction_id
125 }
126
127 fn execute(
128 &self,
129 channel: Channel,
130 request: services::Query,
131 ) -> BoxGrpcFuture<'_, services::Response> {
132 Box::pin(async {
133 CryptoServiceClient::new(channel).get_transaction_receipts(request).await
134 })
135 }
136
137 fn should_retry_pre_check(&self, status: Status) -> bool {
138 matches!(status, Status::ReceiptNotFound | Status::RecordNotFound)
139 }
140
141 fn should_retry(&self, response: &services::Response) -> bool {
142 let receipt_status = {
146 let Some(services::response::Response::TransactionGetReceipt(r)) = &response.response
147 else {
148 return false;
149 };
150
151 match r.receipt.as_ref().and_then(|it| Some(Status::try_from(it.status))) {
152 Some(receipt_status) => receipt_status,
153 None => return false,
154 }
155 };
156
157 matches!(receipt_status, Ok(Status::Unknown))
158 }
159
160 fn make_response(&self, response: Response) -> crate::Result<Self::Response> {
161 let receipt =
162 TransactionReceipt::from_response_protobuf(response, self.transaction_id.as_ref())?;
163
164 if self.validate_status && receipt.status != Status::Success {
165 return Err(Error::ReceiptStatus {
166 transaction_id: self.transaction_id.map(Box::new),
167 status: receipt.status,
168 });
169 }
170
171 Ok(receipt)
172 }
173}
174
175impl ValidateChecksums for TransactionReceiptQueryData {
176 fn validate_checksums(&self, ledger_id: &RefLedgerId) -> Result<(), Error> {
177 self.transaction_id.validate_checksums(ledger_id)
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use expect_test::expect;
184
185 use crate::query::ToQueryProtobuf;
186 use crate::transaction::test_helpers::TEST_TX_ID;
187 use crate::TransactionReceiptQuery;
188
189 #[test]
190 fn serialize() {
191 expect![[r#"
192 Query {
193 query: Some(
194 TransactionGetReceipt(
195 TransactionGetReceiptQuery {
196 header: Some(
197 QueryHeader {
198 payment: None,
199 response_type: AnswerOnly,
200 },
201 ),
202 transaction_id: Some(
203 TransactionId {
204 transaction_valid_start: Some(
205 Timestamp {
206 seconds: 1554158542,
207 nanos: 0,
208 },
209 ),
210 account_id: Some(
211 AccountId {
212 shard_num: 0,
213 realm_num: 0,
214 account: Some(
215 AccountNum(
216 5006,
217 ),
218 ),
219 },
220 ),
221 scheduled: false,
222 nonce: 0,
223 },
224 ),
225 include_duplicates: false,
226 include_child_receipts: false,
227 },
228 ),
229 ),
230 }
231 "#]]
232 .assert_debug_eq(
233 &TransactionReceiptQuery::new()
234 .transaction_id(TEST_TX_ID)
235 .data
236 .to_query_protobuf(Default::default()),
237 )
238 }
239
240 #[test]
241 fn get_set_transaction_id() {
242 let mut query = TransactionReceiptQuery::new();
243 query.transaction_id(TEST_TX_ID);
244
245 assert_eq!(query.get_transaction_id(), Some(TEST_TX_ID));
246 }
247
248 #[test]
250 fn get_set_include_children() {
251 let mut query = TransactionReceiptQuery::new();
252 query.include_children(true);
253
254 assert_eq!(query.get_include_children(), true);
255 }
256
257 #[test]
258 fn get_set_include_duplicates() {
259 let mut query = TransactionReceiptQuery::new();
260 query.include_duplicates(true);
261
262 assert_eq!(query.get_include_duplicates(), true);
263 }
264
265 #[test]
266 fn get_set_validate_status() {
267 let mut query = TransactionReceiptQuery::new();
268 query.validate_status(true);
269
270 assert_eq!(query.get_validate_status(), true);
271 }
272}