Skip to main content

casper_rust_wasm_sdk/sdk/transaction/
transaction.rs

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