Skip to main content

casper_rust_wasm_sdk/sdk/contract/
call_entrypoint.rs

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