casper_rust_wasm_sdk/sdk/transaction/
transaction.rs

1#[cfg(target_arch = "wasm32")]
2use crate::types::hash::transaction_hash::TransactionHash;
3use crate::{
4    types::{
5        sdk_error::SdkError,
6        transaction_params::{
7            transaction_builder_params::{
8                transaction_builder_params_to_casper_client, TransactionBuilderParams,
9            },
10            transaction_str_params::{
11                transaction_str_params_to_casper_client, TransactionStrParams,
12            },
13        },
14        verbosity::Verbosity,
15    },
16    SDK,
17};
18
19use casper_client::{
20    cli::make_transaction, rpcs::results::PutTransactionResult as _PutTransactionResult,
21    SuccessResponse,
22};
23#[cfg(target_arch = "wasm32")]
24use gloo_utils::format::JsValueSerdeExt;
25#[cfg(target_arch = "wasm32")]
26use serde::{Deserialize, Serialize};
27#[cfg(target_arch = "wasm32")]
28use wasm_bindgen::prelude::*;
29
30// Define a struct to wrap the result of a transaction.
31#[cfg(target_arch = "wasm32")]
32#[derive(Debug, Deserialize, Clone, Serialize)]
33#[wasm_bindgen]
34pub struct PutTransactionResult(_PutTransactionResult);
35
36/// Implement conversions between PutTransactionResult and _PutTransactionResult.
37#[cfg(target_arch = "wasm32")]
38impl From<PutTransactionResult> for _PutTransactionResult {
39    fn from(result: PutTransactionResult) -> Self {
40        result.0
41    }
42}
43#[cfg(target_arch = "wasm32")]
44impl From<_PutTransactionResult> for PutTransactionResult {
45    fn from(result: _PutTransactionResult) -> Self {
46        PutTransactionResult(result)
47    }
48}
49
50/// Implement JavaScript bindings for PutTransactionResult.
51#[cfg(target_arch = "wasm32")]
52#[wasm_bindgen]
53impl PutTransactionResult {
54    /// Gets the API version as a JavaScript value.
55    #[wasm_bindgen(getter)]
56    pub fn api_version(&self) -> JsValue {
57        JsValue::from_serde(&self.0.api_version).unwrap()
58    }
59
60    /// Gets the transaction hash associated with this result.
61    #[wasm_bindgen(getter)]
62    pub fn transaction_hash(&self) -> TransactionHash {
63        self.0.transaction_hash.into()
64    }
65
66    /// Converts PutTransactionResult to a JavaScript object.
67    #[wasm_bindgen(js_name = "toJson")]
68    pub fn to_json(&self) -> JsValue {
69        JsValue::from_serde(&self.0).unwrap_or(JsValue::null())
70    }
71}
72
73#[cfg(target_arch = "wasm32")]
74#[wasm_bindgen]
75impl SDK {
76    /// JavaScript function for transactioning with deserialized parameters.
77    ///
78    /// # Arguments
79    ///
80    /// * `transaction_params` - Transaction parameters.
81    /// * `builder_params` - Session parameters.
82    /// * `verbosity` - An optional verbosity level.
83    /// * `rpc_address` - An optional rpc address.
84    ///
85    /// # Returns
86    ///
87    /// A result containing PutTransactionResult or a JsError.
88    #[wasm_bindgen(js_name = "transaction")]
89    pub async fn transaction_js_alias(
90        &self,
91        builder_params: TransactionBuilderParams,
92        transaction_params: TransactionStrParams,
93        verbosity: Option<Verbosity>,
94        rpc_address: Option<String>,
95    ) -> Result<PutTransactionResult, JsError> {
96        let result = self
97            .transaction(builder_params, transaction_params, verbosity, rpc_address)
98            .await;
99        match result {
100            Ok(data) => Ok(data.result.into()),
101            Err(err) => {
102                let err = &format!("Error occurred with {:?}", err);
103                Err(JsError::new(err))
104            }
105        }
106    }
107}
108
109impl SDK {
110    /// Perform a transaction operation.
111    ///
112    /// # Arguments
113    ///
114    /// * `builder_params` - Transaction Builder parameters.
115    /// * `transaction_params` - Transaction parameters.
116    /// * `verbosity` - An optional verbosity level.
117    /// * `rpc_address` - An optional rpc address.
118    ///
119    /// # Returns
120    ///
121    /// A result containing a `SuccessResponse<_PutTransactionResult>` or an SdkError.
122    pub async fn transaction(
123        &self,
124        builder_params: TransactionBuilderParams,
125        transaction_params: TransactionStrParams,
126        verbosity: Option<Verbosity>,
127        rpc_address: Option<String>,
128    ) -> Result<SuccessResponse<_PutTransactionResult>, SdkError> {
129        //log("transaction!");
130        let transaction = match make_transaction(
131            transaction_builder_params_to_casper_client(&builder_params),
132            transaction_str_params_to_casper_client(&transaction_params),
133            false,
134        ) {
135            Ok(transaction) => transaction,
136            Err(err) => {
137                return Err(SdkError::from(err));
138            }
139        };
140        self.put_transaction(transaction.into(), verbosity, rpc_address)
141            .await
142            .map_err(SdkError::from)
143    }
144}
145
146#[cfg(test)]
147mod tests {
148    use std::sync::Mutex;
149
150    use super::*;
151    use crate::helpers::public_key_from_secret_key;
152    use once_cell::sync::Lazy;
153    use sdk_tests::{
154        config::{ARGS_SIMPLE, HELLO_CONTRACT, PAYMENT_AMOUNT, WASM_PATH},
155        tests::helpers::{get_network_constants, get_user_secret_key, read_wasm_file},
156    };
157
158    static ARGS: Lazy<Vec<String>> =
159        Lazy::new(|| ARGS_SIMPLE.iter().map(|s| s.to_string()).collect());
160
161    static BUILDER_PARAMS: Lazy<Mutex<Option<TransactionBuilderParams>>> =
162        Lazy::new(|| Mutex::new(None));
163
164    fn get_builder_params() -> TransactionBuilderParams {
165        let mut builder_params = BUILDER_PARAMS.lock().unwrap();
166
167        if builder_params.is_none() {
168            let file_path = &format!("{WASM_PATH}{HELLO_CONTRACT}");
169            let transaction_bytes = match read_wasm_file(file_path) {
170                Ok(transaction_bytes) => transaction_bytes,
171                Err(err) => {
172                    eprintln!("Error reading file: {:?}", err);
173                    unimplemented!()
174                }
175            };
176
177            let is_install_upgrade = Some(true);
178
179            let new_builder_params = TransactionBuilderParams::new_session(
180                Some(transaction_bytes.into()),
181                is_install_upgrade,
182            );
183            *builder_params = Some(new_builder_params);
184        }
185
186        builder_params.clone().unwrap()
187    }
188
189    #[tokio::test]
190    async fn test_transaction_with_valid_transaction_params() {
191        // Arrange
192        let sdk = SDK::new(None, None, None);
193        let verbosity = Some(Verbosity::High);
194        let (rpc_address, _, _, _, chain_name) = get_network_constants();
195        let secret_key = get_user_secret_key(None).unwrap();
196
197        let mut transaction_params = TransactionStrParams::default();
198        transaction_params.set_secret_key(&secret_key);
199        transaction_params.set_chain_name(&chain_name);
200        transaction_params.set_payment_amount(PAYMENT_AMOUNT);
201        transaction_params.set_session_args_simple(ARGS.to_vec());
202
203        // Act
204        let result = sdk
205            .transaction(
206                get_builder_params().clone(),
207                transaction_params,
208                verbosity,
209                Some(rpc_address),
210            )
211            .await;
212
213        // Assert
214        assert!(result.is_ok());
215    }
216
217    #[tokio::test]
218    async fn test_transaction_with_valid_transaction_params_without_secret_key() {
219        // Arrange
220        let sdk = SDK::new(None, None, None);
221        let verbosity = Some(Verbosity::High);
222        let (rpc_address, _, _, _, chain_name) = get_network_constants();
223        let secret_key = get_user_secret_key(None).unwrap();
224        let initiator_addr = public_key_from_secret_key(&secret_key).unwrap();
225        let error_message = "the transaction was invalid: invalid associated keys";
226
227        let mut transaction_params = TransactionStrParams::default();
228        transaction_params.set_chain_name(&chain_name);
229        transaction_params.set_initiator_addr(&initiator_addr);
230        transaction_params.set_payment_amount(PAYMENT_AMOUNT);
231        transaction_params.set_session_args_simple(ARGS.to_vec());
232
233        // Act
234        let result = sdk
235            .transaction(
236                get_builder_params().clone(),
237                transaction_params,
238                verbosity,
239                Some(rpc_address),
240            )
241            .await;
242
243        // Assert
244        assert!(result.is_err());
245        let err_string = result.err().unwrap().to_string();
246        assert!(err_string.contains(error_message));
247    }
248
249    #[tokio::test]
250    async fn test_transaction_with_invalid_transaction_params() {
251        // Arrange
252        let sdk = SDK::new(None, None, None);
253        let verbosity = Some(Verbosity::High);
254        let (rpc_address, _, _, _, _) = get_network_constants();
255
256        let error_message = "The transaction sent to the network had an invalid chain name";
257        let secret_key = get_user_secret_key(None).unwrap();
258
259        let transaction_params = TransactionStrParams::default();
260        transaction_params.set_payment_amount(PAYMENT_AMOUNT);
261        transaction_params.set_secret_key(&secret_key);
262        transaction_params.set_chain_name("");
263
264        // Act
265        let result = sdk
266            .transaction(
267                get_builder_params().clone(),
268                transaction_params,
269                verbosity,
270                Some(rpc_address),
271            )
272            .await;
273        // Assert
274        assert!(result.is_err());
275
276        let err_string = result.err().unwrap().to_string();
277        assert!(err_string.contains(error_message));
278    }
279}