Skip to main content

casper_rust_wasm_sdk/sdk/rpcs/
get_transaction.rs

1use crate::types::hash::transaction_hash::TransactionHash;
2#[cfg(all(feature = "js", target_arch = "wasm32"))]
3use crate::types::transaction::Transaction;
4use crate::{
5    types::{sdk_error::SdkError, verbosity::Verbosity},
6    SDK,
7};
8use casper_client::{
9    get_transaction, rpcs::results::GetTransactionResult as _GetTransactionResult, JsonRpcId,
10    SuccessResponse,
11};
12#[cfg(all(feature = "js", target_arch = "wasm32"))]
13use gloo_utils::format::JsValueSerdeExt;
14use rand::RngExt;
15#[cfg(all(feature = "js", target_arch = "wasm32"))]
16use serde::{Deserialize, Serialize};
17#[cfg(feature = "js")]
18use wasm_bindgen::prelude::*;
19
20// Define a struct to wrap the GetTransactionResult
21#[cfg(all(feature = "js", target_arch = "wasm32"))]
22#[derive(Debug, Deserialize, Clone, Serialize)]
23#[cfg_attr(feature = "js", wasm_bindgen)]
24pub struct GetTransactionResult(_GetTransactionResult);
25
26#[cfg(all(feature = "js", target_arch = "wasm32"))]
27impl From<GetTransactionResult> for _GetTransactionResult {
28    fn from(result: GetTransactionResult) -> Self {
29        result.0
30    }
31}
32#[cfg(all(feature = "js", target_arch = "wasm32"))]
33impl From<_GetTransactionResult> for GetTransactionResult {
34    fn from(result: _GetTransactionResult) -> Self {
35        GetTransactionResult(result)
36    }
37}
38
39#[cfg(all(feature = "js", target_arch = "wasm32"))]
40#[cfg_attr(feature = "js", wasm_bindgen)]
41impl GetTransactionResult {
42    #[cfg_attr(feature = "js", wasm_bindgen(getter))]
43    /// Gets the API version as a JavaScript value.
44    #[cfg(feature = "js")]
45    pub fn api_version(&self) -> JsValue {
46        JsValue::from_serde(&self.0.api_version).unwrap()
47    }
48
49    #[cfg_attr(feature = "js", wasm_bindgen(getter))]
50    /// Gets the transaction information.
51    pub fn transaction(&self) -> Transaction {
52        self.0.transaction.clone().into()
53    }
54
55    #[cfg_attr(feature = "js", wasm_bindgen(getter))]
56    /// Gets the execution info as a JavaScript value.
57    #[cfg(feature = "js")]
58    pub fn execution_info(&self) -> JsValue {
59        JsValue::from_serde(&self.0.execution_info).unwrap()
60    }
61
62    #[cfg_attr(feature = "js", wasm_bindgen(js_name = "toJson"))]
63    /// Converts the result to a JSON JavaScript value.
64    #[cfg(feature = "js")]
65    pub fn to_json(&self) -> JsValue {
66        JsValue::from_serde(&self.0).unwrap_or(JsValue::null())
67    }
68}
69
70/// Options for the `get_transaction` method.
71#[derive(Debug, Clone, Default, Deserialize, Serialize)]
72#[cfg(all(feature = "js", target_arch = "wasm32"))]
73#[cfg_attr(
74    feature = "js",
75    wasm_bindgen(js_name = "getTransactionOptions", getter_with_clone)
76)]
77pub struct GetTransactionOptions {
78    pub transaction_hash_as_string: Option<String>,
79    pub transaction_hash: Option<TransactionHash>,
80    pub finalized_approvals: Option<bool>,
81    pub rpc_address: Option<String>,
82    pub verbosity: Option<Verbosity>,
83}
84
85#[cfg_attr(feature = "js", wasm_bindgen)]
86impl SDK {
87    /// Parses transaction options from a JsValue.
88    ///
89    /// # Arguments
90    ///
91    /// * `options` - A JsValue containing transaction options to be parsed.
92    ///
93    /// # Returns
94    ///
95    /// Parsed transaction options as a `GetTransactionOptions` struct.
96    #[cfg(all(feature = "js", target_arch = "wasm32"))]
97    pub fn get_transaction_options(
98        &self,
99        options: JsValue,
100    ) -> Result<GetTransactionOptions, JsError> {
101        let options_result: Result<GetTransactionOptions, _> = options.into_serde();
102        match options_result {
103            Ok(mut options) => {
104                if let Some(finalized_approvals) = options.finalized_approvals {
105                    options.finalized_approvals =
106                        Some(JsValue::from_bool(finalized_approvals) == JsValue::TRUE);
107                }
108                Ok(options)
109            }
110            Err(err) => Err(JsError::new(&format!(
111                "Error deserializing options: {err:?}"
112            ))),
113        }
114    }
115
116    /// Retrieves transaction information using the provided options.
117    ///
118    /// # Arguments
119    ///
120    /// * `options` - An optional `GetTransactionOptions` struct containing retrieval options.
121    ///
122    /// # Returns
123    ///
124    /// A `Result` containing either a `GetTransactionResult` or an error.
125    #[cfg(all(feature = "js", target_arch = "wasm32"))]
126    #[cfg_attr(feature = "js", wasm_bindgen(js_name = "get_transaction"))]
127    pub async fn get_transaction_js_alias(
128        &self,
129        options: Option<GetTransactionOptions>,
130    ) -> Result<GetTransactionResult, JsError> {
131        let GetTransactionOptions {
132            transaction_hash_as_string,
133            transaction_hash,
134            finalized_approvals,
135            verbosity,
136            rpc_address,
137        } = options.unwrap_or_default();
138
139        let transaction_hash = if let Some(transaction_hash_as_string) = transaction_hash_as_string
140        {
141            TransactionHash::new(&transaction_hash_as_string)?
142        } else {
143            if transaction_hash.is_none() {
144                let err_msg =
145                    "Error: Missing transaction hash as string or transaction hash".to_string();
146                return Err(JsError::new(&err_msg));
147            }
148            transaction_hash.unwrap()
149        };
150
151        let result = self
152            .get_transaction(
153                transaction_hash,
154                finalized_approvals,
155                verbosity,
156                rpc_address,
157            )
158            .await;
159        match result {
160            Ok(data) => Ok(data.result.into()),
161            Err(err) => {
162                let err = &format!("Error occurred with {err:?}");
163                Err(JsError::new(err))
164            }
165        }
166    }
167
168    /// Retrieves transaction information using the provided options, alias for `get_transaction`.
169    #[cfg(all(feature = "js", target_arch = "wasm32"))]
170    #[deprecated(note = "This function is an alias. Please use `get_transaction` instead.")]
171    #[allow(deprecated)]
172    pub async fn info_get_transaction(
173        &self,
174        options: Option<GetTransactionOptions>,
175    ) -> Result<GetTransactionResult, JsError> {
176        self.get_transaction_js_alias(options).await
177    }
178}
179
180impl SDK {
181    /// Retrieves transaction information based on the provided options.
182    ///
183    /// # Arguments
184    ///
185    /// * `transaction_hash` - The transaction hash.
186    /// * `finalized_approvals` - An optional boolean indicating finalized approvals.
187    /// * `verbosity` - An optional verbosity level.
188    /// * `rpc_address` - An optional rpc address.
189    ///
190    /// # Returns
191    ///
192    /// A `Result` containing either a `_GetTransactionResult` or an error.
193    pub async fn get_transaction(
194        &self,
195        transaction_hash: TransactionHash,
196        finalized_approvals: Option<bool>,
197        verbosity: Option<Verbosity>,
198        rpc_address: Option<String>,
199    ) -> Result<SuccessResponse<_GetTransactionResult>, SdkError> {
200        //log("get_transaction!");
201        let random_id = JsonRpcId::from(rand::rng().random::<u64>().to_string());
202        get_transaction(
203            random_id,
204            &self.get_rpc_address(rpc_address),
205            self.get_verbosity(verbosity).into(),
206            transaction_hash.into(),
207            finalized_approvals.unwrap_or_default(),
208        )
209        .await
210        .map_err(Into::into)
211    }
212}
213
214#[cfg(test)]
215mod tests {
216    use super::*;
217    use crate::{
218        helpers::public_key_from_secret_key,
219        types::transaction_params::transaction_str_params::TransactionStrParams,
220    };
221    use sdk_tests::{
222        config::TRANSFER_AMOUNT,
223        tests::helpers::{get_network_constants, get_user_secret_key},
224    };
225
226    #[tokio::test]
227    async fn test_get_transaction_with_none_values() {
228        // Arrange
229        let sdk = SDK::new(None, None, None);
230        let transaction_hash = TransactionHash::from_raw(&[1u8; 32]).unwrap();
231        let error_message = "failed to parse node address as valid URL";
232
233        // Act
234        let result = sdk
235            .get_transaction(transaction_hash, None, None, None)
236            .await;
237
238        // Assert
239        assert!(result.is_err());
240        let err_string = result.err().unwrap().to_string();
241        assert!(err_string.contains(error_message));
242    }
243
244    #[tokio::test]
245    async fn test_get_transaction_with_invalid_transaction_hash() {
246        // Arrange
247        let sdk = SDK::new(None, None, None);
248        let transaction_hash = TransactionHash::from_raw(&[1u8; 32]).unwrap();
249        let verbosity = Some(Verbosity::High);
250        let (rpc_address, _, _, _, _) = get_network_constants();
251
252        // Act
253        let result = sdk
254            .get_transaction(transaction_hash, None, verbosity, Some(rpc_address))
255            .await;
256
257        // Assert
258        assert!(result.is_err());
259    }
260
261    #[tokio::test]
262    async fn test_get_transaction_with_valid_transaction_hash() {
263        // Arrange
264        let sdk = SDK::new(None, None, None);
265        let verbosity = Some(Verbosity::High);
266        let (rpc_address, _, _, _, chain_name) = get_network_constants();
267
268        let secret_key = get_user_secret_key(None).unwrap();
269        let initiator_addr = public_key_from_secret_key(&secret_key).unwrap();
270
271        let transaction_params = TransactionStrParams::default();
272        transaction_params.set_secret_key(&secret_key);
273        transaction_params.set_chain_name(&chain_name);
274        transaction_params.set_payment_amount(TRANSFER_AMOUNT);
275
276        let transfer = sdk
277            .transfer_transaction(
278                None,
279                &initiator_addr, // self transfer
280                TRANSFER_AMOUNT,
281                transaction_params,
282                None,
283                verbosity,
284                Some(rpc_address.clone()),
285            )
286            .await
287            .unwrap();
288        let transaction_hash = transfer.result.transaction_hash;
289        assert!(!transaction_hash.to_string().is_empty());
290
291        // Act
292        let result = sdk
293            .get_transaction(transaction_hash.into(), None, verbosity, Some(rpc_address))
294            .await;
295
296        // Assert
297        // dbg!(result);
298        assert!(result.is_ok());
299    }
300
301    #[tokio::test]
302    async fn test_get_transaction_with_finalized_approvals() {
303        // Arrange
304        let sdk = SDK::new(None, None, None);
305        let verbosity = Some(Verbosity::High);
306        let (rpc_address, _, _, _, chain_name) = get_network_constants();
307
308        let secret_key = get_user_secret_key(None).unwrap();
309        let initiator_addr = public_key_from_secret_key(&secret_key).unwrap();
310
311        let transaction_params = TransactionStrParams::default();
312        transaction_params.set_secret_key(&secret_key);
313        transaction_params.set_chain_name(&chain_name);
314        transaction_params.set_payment_amount(TRANSFER_AMOUNT);
315
316        let transfer = sdk
317            .transfer_transaction(
318                None,
319                &initiator_addr, // self transfer
320                TRANSFER_AMOUNT,
321                transaction_params,
322                None,
323                verbosity,
324                Some(rpc_address.clone()),
325            )
326            .await
327            .unwrap();
328        let transaction_hash = transfer.result.transaction_hash;
329        assert!(!transaction_hash.to_string().is_empty());
330        let finalized_approvals = true;
331
332        // Act
333        let result = sdk
334            .get_transaction(
335                transaction_hash.into(),
336                Some(finalized_approvals),
337                verbosity,
338                Some(rpc_address),
339            )
340            .await;
341
342        // Assert
343        assert!(result.is_ok());
344    }
345
346    #[tokio::test]
347    async fn test_get_transaction_with_error() {
348        // Arrange
349        let sdk = SDK::new(Some("http://localhost".to_string()), None, None);
350        let transaction_hash = TransactionHash::from_raw(&[1u8; 32]).unwrap();
351        let error_message = "error sending request";
352
353        // Act
354        let result = sdk
355            .get_transaction(transaction_hash, None, None, None)
356            .await;
357
358        // Assert
359        assert!(result.is_err());
360        let err_string = result.err().unwrap().to_string();
361        assert!(err_string.contains(error_message));
362    }
363}