Skip to main content

casper_rust_wasm_sdk/sdk/rpcs/
get_account.rs

1#[cfg(all(feature = "js", target_arch = "wasm32"))]
2use crate::types::identifier::block_identifier::BlockIdentifier;
3use crate::{
4    types::{
5        account::Account,
6        identifier::{
7            account_identifier::AccountIdentifier, block_identifier::BlockIdentifierInput,
8        },
9        sdk_error::SdkError,
10        verbosity::Verbosity,
11    },
12    SDK,
13};
14use casper_client::cli::parse::account_identifier as parse_account_identifier;
15use casper_client::{
16    cli::get_account as get_account_cli, get_account as get_account_lib,
17    rpcs::results::GetAccountResult as _GetAccountResult, JsonRpcId, SuccessResponse,
18};
19#[cfg(all(feature = "js", target_arch = "wasm32"))]
20use gloo_utils::format::JsValueSerdeExt;
21use rand::RngExt;
22use serde::{Deserialize, Serialize};
23#[cfg(feature = "js")]
24use wasm_bindgen::prelude::*;
25
26/// Wrapper around Casper Client `GetAccountResult`.
27#[derive(Debug, Deserialize, Clone, Serialize)]
28#[cfg_attr(feature = "js", wasm_bindgen)]
29pub struct GetAccountResult(_GetAccountResult);
30
31impl From<GetAccountResult> for _GetAccountResult {
32    fn from(result: GetAccountResult) -> Self {
33        result.0
34    }
35}
36
37impl From<_GetAccountResult> for GetAccountResult {
38    fn from(result: _GetAccountResult) -> Self {
39        GetAccountResult(result)
40    }
41}
42
43impl GetAccountResult {
44    /// Gets the typed account wrapper.
45    pub fn account_typed(&self) -> Account {
46        self.0.account.clone().into()
47    }
48}
49
50#[cfg(all(feature = "js", target_arch = "wasm32"))]
51#[cfg_attr(feature = "js", wasm_bindgen)]
52impl GetAccountResult {
53    #[cfg_attr(feature = "js", wasm_bindgen(getter))]
54    #[cfg(feature = "js")]
55    pub fn api_version(&self) -> JsValue {
56        JsValue::from_serde(&self.0.api_version).unwrap()
57    }
58
59    #[cfg_attr(feature = "js", wasm_bindgen(getter))]
60    #[cfg(feature = "js")]
61    pub fn account(&self) -> JsValue {
62        JsValue::from_serde(&self.0.account).unwrap()
63    }
64
65    /// Gets the typed account wrapper.
66    #[cfg_attr(feature = "js", wasm_bindgen(js_name = "accountTyped"))]
67    pub fn account_typed_js(&self) -> Account {
68        self.account_typed()
69    }
70
71    #[cfg_attr(feature = "js", wasm_bindgen(getter))]
72    pub fn merkle_proof(&self) -> String {
73        self.0.merkle_proof.clone()
74    }
75
76    #[cfg_attr(feature = "js", wasm_bindgen(js_name = "toJson"))]
77    #[cfg(feature = "js")]
78    pub fn to_json(&self) -> JsValue {
79        JsValue::from_serde(&self.0).unwrap_or(JsValue::null())
80    }
81}
82
83// Define options for the `get_account` function
84#[derive(Debug, Deserialize, Clone, Default, Serialize)]
85#[cfg(all(feature = "js", target_arch = "wasm32"))]
86#[cfg_attr(
87    feature = "js",
88    wasm_bindgen(js_name = "getAccountOptions", getter_with_clone)
89)]
90pub struct GetAccountOptions {
91    pub account_identifier: Option<AccountIdentifier>,
92    pub account_identifier_as_string: Option<String>,
93    pub maybe_block_id_as_string: Option<String>,
94    pub maybe_block_identifier: Option<BlockIdentifier>,
95    pub rpc_address: Option<String>,
96    pub verbosity: Option<Verbosity>,
97}
98
99#[cfg(all(feature = "js", target_arch = "wasm32"))]
100#[cfg_attr(feature = "js", wasm_bindgen)]
101impl SDK {
102    // Deserialize options for `get_account` from a JavaScript object
103    #[deprecated(note = "prefer 'get_entity_options'")]
104    #[allow(deprecated)]
105    #[cfg(feature = "js")]
106    pub fn get_account_options(&self, options: JsValue) -> Result<GetAccountOptions, JsError> {
107        options
108            .into_serde::<GetAccountOptions>()
109            .map_err(|err| JsError::new(&format!("Error deserializing options: {err:?}")))
110    }
111
112    /// Retrieves account information using the provided options.
113    ///
114    /// This function is an asynchronous JavaScript binding for the Rust `get_account` method.
115    ///
116    /// # Arguments
117    ///
118    /// * `options` - An optional `GetAccountOptions` struct containing retrieval options, such as:
119    ///   - `account_identifier`: Identifier for the account.
120    ///   - `account_identifier_as_string`: String representation of the account identifier.
121    ///   - `maybe_block_id_as_string`: Optional string representation of the block ID.
122    ///   - `maybe_block_identifier`: Optional `BlockIdentifierInput` for specifying the block.
123    ///   - `verbosity`: Verbosity level for the output.
124    ///   - `rpc_address`: Address of the node to query.
125    ///
126    /// # Returns
127    ///
128    /// A `Result` containing either a `GetAccountResult` on success or a `JsError` on failure.
129    ///
130    /// # Errors
131    ///
132    /// Returns a `JsError` if there is an error during the retrieval process, such as issues with the provided options or network errors.
133    /// ```
134    #[cfg_attr(feature = "js", wasm_bindgen(js_name = "get_account"))]
135    #[deprecated(note = "prefer 'get_entity'")]
136    #[allow(deprecated)]
137    #[cfg(feature = "js")]
138    pub async fn get_account_js_alias(
139        &self,
140        options: Option<GetAccountOptions>,
141    ) -> Result<GetAccountResult, JsError> {
142        let GetAccountOptions {
143            account_identifier,
144            account_identifier_as_string,
145            maybe_block_id_as_string,
146            maybe_block_identifier,
147            verbosity,
148            rpc_address,
149        } = options.unwrap_or_default();
150
151        let maybe_block_identifier = if let Some(maybe_block_identifier) = maybe_block_identifier {
152            Some(BlockIdentifierInput::BlockIdentifier(
153                maybe_block_identifier,
154            ))
155        } else {
156            maybe_block_id_as_string.map(BlockIdentifierInput::String)
157        };
158
159        let result = self
160            .get_account(
161                account_identifier,
162                account_identifier_as_string,
163                maybe_block_identifier,
164                verbosity,
165                rpc_address,
166            )
167            .await;
168        match result {
169            Ok(data) => Ok(data.result.into()),
170            Err(err) => {
171                let err = &format!("Error occurred with {err:?}");
172                Err(JsError::new(err))
173            }
174        }
175    }
176
177    // JavaScript alias for `get_account`
178    #[cfg_attr(feature = "js", wasm_bindgen(js_name = "state_get_account_info"))]
179    #[deprecated(note = "prefer 'get_entity'")]
180    #[allow(deprecated)]
181    #[cfg(feature = "js")]
182    pub async fn state_get_account_info(
183        &self,
184        options: Option<GetAccountOptions>,
185    ) -> Result<GetAccountResult, JsError> {
186        self.get_account_js_alias(options).await
187    }
188}
189
190impl SDK {
191    /// Retrieves account information based on the provided options.
192    ///
193    /// # Arguments
194    ///
195    /// * `account_identifier` - An optional `AccountIdentifier` for specifying the account identifier.
196    /// * `account_identifier_as_string` - An optional string representing the account identifier.
197    /// * `maybe_block_identifier` - An optional `BlockIdentifierInput` for specifying a block identifier.
198    /// * `verbosity` - An optional `Verbosity` level for controlling the output verbosity.
199    /// * `rpc_address` - An optional string specifying the rpc address to use for the request.
200    ///
201    /// # Returns
202    ///
203    /// A `Result` containing either a `SuccessResponse<_GetAccountResult>` or a `SdkError` in case of an error.
204    ///
205    /// # Errors
206    ///
207    /// Returns a `SdkError` if there is an error during the retrieval process.
208    #[deprecated(note = "prefer 'get_entity'")]
209    #[allow(deprecated)]
210    pub async fn get_account(
211        &self,
212        account_identifier: Option<AccountIdentifier>,
213        account_identifier_as_string: Option<String>,
214        maybe_block_identifier: Option<BlockIdentifierInput>,
215        verbosity: Option<Verbosity>,
216        rpc_address: Option<String>,
217    ) -> Result<SuccessResponse<_GetAccountResult>, SdkError> {
218        let account_identifier = if let Some(account_identifier) = account_identifier {
219            account_identifier
220        } else if let Some(account_identifier_as_string) = account_identifier_as_string.clone() {
221            match parse_account_identifier(&account_identifier_as_string) {
222                Ok(parsed) => parsed.into(),
223                Err(err) => {
224                    return Err(err.into());
225                }
226            }
227        } else {
228            let err = "Error: Missing account identifier".to_string();
229            return Err(SdkError::InvalidArgument {
230                context: "get_account",
231                error: err,
232            });
233        };
234        let random_id = rand::rng().random::<u64>().to_string();
235        if let Some(BlockIdentifierInput::String(maybe_block_id)) = maybe_block_identifier {
236            get_account_cli(
237                &random_id,
238                &self.get_rpc_address(rpc_address),
239                self.get_verbosity(verbosity).into(),
240                &maybe_block_id,
241                &account_identifier.to_string(),
242            )
243            .await
244            .map_err(SdkError::from)
245        } else {
246            let maybe_block_identifier =
247                if let Some(BlockIdentifierInput::BlockIdentifier(maybe_block_identifier)) =
248                    maybe_block_identifier
249                {
250                    Some(maybe_block_identifier)
251                } else {
252                    None
253                };
254            get_account_lib(
255                JsonRpcId::from(random_id),
256                &self.get_rpc_address(rpc_address),
257                self.get_verbosity(verbosity).into(),
258                maybe_block_identifier.map(Into::into),
259                account_identifier.into(),
260            )
261            .await
262            .map_err(SdkError::from)
263        }
264    }
265}
266
267#[cfg(test)]
268mod tests {
269    use super::*;
270    use crate::{
271        helpers::public_key_from_secret_key,
272        types::{identifier::block_identifier::BlockIdentifier, public_key::PublicKey},
273    };
274    use sdk_tests::tests::helpers::{
275        get_enable_addressable_entity, get_network_constants, get_user_secret_key,
276    };
277
278    #[allow(deprecated)]
279    fn get_account_identifier() -> AccountIdentifier {
280        let secret_key = get_user_secret_key(None).unwrap();
281        let account = public_key_from_secret_key(&secret_key).unwrap();
282        let public_key = PublicKey::new(&account).unwrap();
283
284        AccountIdentifier::from_account_under_public_key(public_key)
285    }
286
287    #[tokio::test]
288    #[allow(deprecated)]
289    async fn test_get_account_with_none_values() {
290        // Arrange
291        let sdk = SDK::new(None, None, None);
292        let error_message = "failed to parse node address as valid URL";
293        let account_identifier = get_account_identifier();
294
295        // Act
296        let result = sdk
297            .get_account(Some(account_identifier), None, None, None, None)
298            .await;
299
300        // Assert
301        assert!(result.is_err());
302        let err_string = result.err().unwrap().to_string();
303        assert!(err_string.contains(error_message));
304    }
305
306    #[tokio::test]
307    #[allow(deprecated)]
308    async fn test_get_account_with_missing_account() {
309        // Arrange
310        let sdk = SDK::new(None, None, None);
311        let error_message = "Error: Missing account identifier";
312
313        // Act
314        let result = sdk.get_account(None, None, None, None, None).await;
315
316        // Assert
317        assert!(result.is_err());
318        let err_string = result.err().unwrap().to_string();
319        assert!(err_string.contains(error_message));
320    }
321
322    #[tokio::test]
323    #[allow(deprecated)]
324    async fn _test_get_account_with_account_identifier() {
325        if get_enable_addressable_entity() {
326            return;
327        }
328        // Arrange
329        let sdk = SDK::new(None, None, None);
330        let account_identifier = get_account_identifier();
331        let verbosity = Some(Verbosity::High);
332        let (rpc_address, _, _, _, _) = get_network_constants();
333
334        // Act
335        let result = sdk
336            .get_account(
337                Some(account_identifier),
338                None,
339                None,
340                verbosity,
341                Some(rpc_address),
342            )
343            .await;
344        // Assert
345        assert!(result.is_ok());
346    }
347
348    #[tokio::test]
349    #[allow(deprecated)]
350    async fn _test_get_account_with_account_identifier_as_string() {
351        if get_enable_addressable_entity() {
352            return;
353        }
354        // Arrange
355        let sdk = SDK::new(None, None, None);
356        let account_identifier_as_string = get_account_identifier().to_string();
357        let verbosity = Some(Verbosity::High);
358        let (rpc_address, _, _, _, _) = get_network_constants();
359
360        // Act
361        let result = sdk
362            .get_account(
363                None,
364                Some(account_identifier_as_string),
365                None,
366                verbosity,
367                Some(rpc_address),
368            )
369            .await;
370
371        // Assert
372        assert!(result.is_ok());
373    }
374
375    #[tokio::test]
376    #[allow(deprecated)]
377    async fn _test_get_account_with_block_identifier() {
378        if get_enable_addressable_entity() {
379            return;
380        }
381        // Arrange
382        let sdk = SDK::new(None, None, None);
383        let block_identifier =
384            BlockIdentifierInput::BlockIdentifier(BlockIdentifier::from_height(1));
385        let account_identifier = get_account_identifier();
386        let verbosity = Some(Verbosity::High);
387        let (rpc_address, _, _, _, _) = get_network_constants();
388
389        // Act
390        let result = sdk
391            .get_account(
392                Some(account_identifier),
393                None,
394                Some(block_identifier),
395                verbosity,
396                Some(rpc_address),
397            )
398            .await;
399
400        // Assert
401        assert!(result.is_ok());
402    }
403
404    #[tokio::test]
405    #[allow(deprecated)]
406    async fn test_get_account_with_error() {
407        let sdk = SDK::new(Some("http://localhost".to_string()), None, None);
408        let account_identifier = get_account_identifier();
409        let error_message = "error sending request";
410
411        let result = sdk
412            .get_account(Some(account_identifier), None, None, None, None)
413            .await;
414
415        assert!(result.is_err());
416        let err_string = result.err().unwrap().to_string();
417        assert!(err_string.contains(error_message));
418    }
419
420    #[tokio::test]
421    #[allow(deprecated)]
422    async fn test_get_account_typed_legacy() {
423        if get_enable_addressable_entity() {
424            return;
425        }
426        let sdk = SDK::new(None, None, None);
427        let account_identifier = get_account_identifier();
428        let verbosity = Some(Verbosity::High);
429        let (rpc_address, _, _, _, _) = get_network_constants();
430
431        let result = sdk
432            .get_account(
433                Some(account_identifier),
434                None,
435                None,
436                verbosity,
437                Some(rpc_address),
438            )
439            .await
440            .expect("get_account");
441
442        let account = GetAccountResult::from(result.result).account_typed();
443        assert!(!account.account_hash().to_formatted_string().is_empty());
444        assert!(!account.main_purse().to_formatted_string().is_empty());
445    }
446}