Skip to main content

casper_rust_wasm_sdk/sdk/contract/
install.rs

1#[cfg(all(feature = "js", target_arch = "wasm32"))]
2use crate::transaction::transaction::PutTransactionResult;
3use crate::{
4    types::{
5        cl::bytes::Bytes,
6        sdk_error::SdkError,
7        transaction_params::{
8            transaction_builder_params::TransactionBuilderParams,
9            transaction_str_params::TransactionStrParams,
10        },
11    },
12    SDK,
13};
14use casper_client::{
15    rpcs::results::PutTransactionResult as _PutTransactionResult, SuccessResponse,
16};
17#[cfg(all(feature = "js", target_arch = "wasm32"))]
18use wasm_bindgen::prelude::*;
19
20/// A set of functions for installing smart contracts on the blockchain.
21#[cfg(all(feature = "js", target_arch = "wasm32"))]
22#[cfg_attr(feature = "js", wasm_bindgen)]
23impl SDK {
24    /// Installs a smart contract with the specified parameters and returns the result.
25    ///
26    /// # Arguments
27    ///.
28    /// * `transaction_params` - Transaction parameters.
29    /// * `transaction_bytes` - Transaction Bytes to install
30    /// * `rpc_address` - An optional rpc address to send the request to.
31    ///
32    /// # Returns
33    ///
34    /// A `Result` containing either a `PutTransactionResult` or a `JsError` in case of an error.
35    ///
36    /// # Errors
37    ///
38    /// Returns a `JsError` if there is an error during the installation.
39    #[cfg_attr(feature = "js", wasm_bindgen(js_name = "install"))]
40    #[cfg(feature = "js")]
41    pub async fn install_js_alias(
42        &self,
43        transaction_params: TransactionStrParams,
44        transaction_bytes: Bytes,
45        rpc_address: Option<String>,
46        runtime_v2: Option<bool>,
47    ) -> Result<PutTransactionResult, JsError> {
48        let result = self
49            .install(
50                transaction_params,
51                transaction_bytes,
52                rpc_address,
53                runtime_v2,
54            )
55            .await;
56        match result {
57            Ok(data) => Ok(data.result.into()),
58            Err(err) => {
59                let err = &format!("Error occurred with {err:?}");
60                Err(JsError::new(err))
61            }
62        }
63    }
64}
65
66/// A set of functions for installing smart contracts on the blockchain.
67/// Alias of sdk.transaction
68impl SDK {
69    /// Installs session wasm (`is_install_upgrade`) and returns the put result.
70    ///
71    /// Session runtime defaults to VmCasperV2 (`runtime_v2` omitted or `true`).
72    /// Pass `runtime_v2: false` for classic VmCasperV1 wasm (HELLO, CEP-78, …).
73    ///
74    /// # Arguments
75    ///
76    /// * `transaction_params` - Transaction parameters.
77    /// * `transaction_bytes` - Transaction Bytes to install
78    /// * `rpc_address` - An optional rpc address to send the request to.
79    /// * `runtime_v2` - `None`/`true` → VmCasperV2; `false` → VmCasperV1.
80    ///
81    /// # Returns
82    ///
83    /// A `Result` containing either a `_PutTransactionResult` or a `SdkError` in case of an error.
84    ///
85    /// # Errors
86    ///
87    /// Returns a `SdkError` if there is an error during the installation.
88    pub async fn install(
89        &self,
90        transaction_params: TransactionStrParams,
91        transaction_bytes: Bytes,
92        rpc_address: Option<String>,
93        runtime_v2: Option<bool>,
94    ) -> Result<SuccessResponse<_PutTransactionResult>, SdkError> {
95        //log("install!");
96        let is_install_upgrade = Some(true);
97        let mut builder_params =
98            TransactionBuilderParams::new_session(Some(transaction_bytes), is_install_upgrade);
99        apply_runtime_v2(&mut builder_params, runtime_v2);
100        self.transaction(builder_params, transaction_params, None, rpc_address)
101            .await
102    }
103}
104
105fn apply_runtime_v2(builder_params: &mut TransactionBuilderParams, runtime_v2: Option<bool>) {
106    match runtime_v2 {
107        None | Some(true) => {}
108        Some(false) => builder_params.set_runtime_v1(),
109    }
110}
111
112#[cfg(test)]
113mod tests {
114    use super::*;
115    use crate::helpers::public_key_from_secret_key;
116    use once_cell::sync::Lazy;
117    use sdk_tests::{
118        config::{ARGS_SIMPLE, HELLO_CONTRACT, PAYMENT_AMOUNT, WASM_PATH},
119        tests::helpers::{get_network_constants, get_user_secret_key, read_wasm_file},
120    };
121    use tokio;
122
123    static ARGS: Lazy<Vec<String>> =
124        Lazy::new(|| ARGS_SIMPLE.iter().map(|s| s.to_string()).collect());
125
126    #[tokio::test]
127    async fn test_install_with_none_values() {
128        // Arrange
129        let sdk = SDK::new(None, None, None);
130        let transaction_params = TransactionStrParams::default();
131        transaction_params.set_payment_amount(PAYMENT_AMOUNT);
132        let transaction_bytes = Bytes::default();
133
134        let error_message =
135            "transaction requires account - use `with_account` or `with_secret_key`";
136
137        // Act
138        let result = sdk
139            .install(transaction_params, transaction_bytes, None, None)
140            .await;
141
142        // Assert
143        assert!(result.is_err());
144        let err_string = result.err().unwrap().to_string();
145
146        assert!(err_string.contains(error_message));
147    }
148
149    #[tokio::test]
150    async fn test_install_with_valid_input() {
151        // Arrange
152        let sdk = SDK::new(None, None, None);
153        let (rpc_address, _, _, _, chain_name) = get_network_constants();
154        let secret_key = get_user_secret_key(None).unwrap();
155
156        let mut transaction_params = TransactionStrParams::default();
157        transaction_params.set_secret_key(&secret_key);
158        transaction_params.set_chain_name(&chain_name);
159        transaction_params.set_payment_amount(PAYMENT_AMOUNT);
160        transaction_params.set_session_args_simple(ARGS.to_vec());
161
162        let transaction_bytes = match read_wasm_file(&format!("{WASM_PATH}{HELLO_CONTRACT}")) {
163            Ok(transaction_bytes) => transaction_bytes,
164            Err(err) => {
165                eprintln!("Error reading file: {err:?}");
166                return;
167            }
168        };
169
170        // Act
171        let result = sdk
172            .install(
173                transaction_params,
174                transaction_bytes.into(),
175                Some(rpc_address),
176                Some(false),
177            )
178            .await;
179
180        // Assert
181        assert!(result.is_ok());
182        let transaction_hash = result.unwrap().result.transaction_hash;
183        assert!(!transaction_hash.to_string().is_empty());
184    }
185
186    #[tokio::test]
187    async fn test_install_with_invalid_input() {
188        // Arrange
189        let sdk = SDK::new(None, None, None);
190        let (rpc_address, _, _, _, _) = get_network_constants();
191        let secret_key = get_user_secret_key(None).unwrap();
192
193        let error_message = "The transaction sent to the network had an invalid chain name";
194
195        let mut transaction_params = TransactionStrParams::default();
196        transaction_params.set_secret_key(&secret_key);
197        transaction_params.set_chain_name("test");
198        transaction_params.set_session_args_simple(ARGS.to_vec());
199        transaction_params.set_payment_amount(PAYMENT_AMOUNT);
200
201        let transaction_bytes = match read_wasm_file(&format!("{WASM_PATH}{HELLO_CONTRACT}")) {
202            Ok(transaction_bytes) => transaction_bytes,
203            Err(err) => {
204                eprintln!("Error reading file: {err:?}");
205                return;
206            }
207        };
208
209        // Act
210        let result = sdk
211            .install(
212                transaction_params,
213                transaction_bytes.into(),
214                Some(rpc_address),
215                Some(false),
216            )
217            .await;
218
219        // Assert
220        assert!(result.is_err());
221        let err_string = result.err().unwrap().to_string();
222        assert!(err_string.contains(error_message));
223    }
224
225    #[tokio::test]
226    async fn test_install_without_secret_key() {
227        // Arrange
228        let sdk = SDK::new(None, None, None);
229        let (rpc_address, _, _, _, chain_name) = get_network_constants();
230        let secret_key = get_user_secret_key(None).unwrap();
231        let initiator_addr = public_key_from_secret_key(&secret_key).unwrap();
232
233        let error_message = "the transaction was invalid: invalid associated keys";
234
235        let mut transaction_params = TransactionStrParams::default();
236        transaction_params.set_initiator_addr(&initiator_addr);
237        transaction_params.set_chain_name(&chain_name);
238        transaction_params.set_payment_amount(PAYMENT_AMOUNT);
239        transaction_params.set_session_args_simple(ARGS.to_vec());
240
241        let transaction_bytes = match read_wasm_file(&format!("{WASM_PATH}{HELLO_CONTRACT}")) {
242            Ok(transaction_bytes) => transaction_bytes,
243            Err(err) => {
244                eprintln!("Error reading file: {err:?}");
245                return;
246            }
247        };
248
249        // Act
250        let result = sdk
251            .install(
252                transaction_params,
253                transaction_bytes.into(),
254                Some(rpc_address),
255                Some(false),
256            )
257            .await;
258
259        // Assert
260        assert!(result.is_err());
261        let err_string = result.err().unwrap().to_string();
262        assert!(err_string.contains(error_message));
263    }
264
265    #[tokio::test]
266    async fn test_install_with_error() {
267        // Arrange
268        let sdk = SDK::new(Some("http://localhost".to_string()), None, None);
269        let (_, _, _, _, chain_name) = get_network_constants();
270        let secret_key = get_user_secret_key(None).unwrap();
271
272        let error_message = "error sending request";
273
274        let mut transaction_params = TransactionStrParams::default();
275        transaction_params.set_secret_key(&secret_key);
276        transaction_params.set_chain_name(&chain_name);
277        transaction_params.set_payment_amount(PAYMENT_AMOUNT);
278        transaction_params.set_session_args_simple(ARGS.to_vec());
279
280        let transaction_bytes = match read_wasm_file(&format!("{WASM_PATH}{HELLO_CONTRACT}")) {
281            Ok(transaction_bytes) => transaction_bytes,
282            Err(err) => {
283                eprintln!("Error reading file: {err:?}");
284                return;
285            }
286        };
287        // Act
288        let result = sdk
289            .install(
290                transaction_params,
291                transaction_bytes.into(),
292                None,
293                Some(false),
294            )
295            .await;
296
297        // Assert
298        assert!(result.is_err());
299        let err_string = result.err().unwrap().to_string();
300        assert!(err_string.contains(error_message));
301    }
302}