casper_rust_wasm_sdk/sdk/rpcs/
get_dictionary_item.rs1use crate::types::digest::Digest;
2use crate::{
3 types::{
4 deploy_params::dictionary_item_str_params::{
5 dictionary_item_str_params_to_casper_client, DictionaryItemStrParams,
6 },
7 digest::ToDigest,
8 identifier::dictionary_item_identifier::DictionaryItemIdentifier,
9 sdk_error::SdkError,
10 stored_value::StoredValue,
11 verbosity::Verbosity,
12 },
13 SDK,
14};
15use casper_client::{
16 cli::get_dictionary_item as get_dictionary_item_cli,
17 get_dictionary_item as get_dictionary_item_lib,
18 rpcs::results::GetDictionaryItemResult as _GetDictionaryItemResult, JsonRpcId, SuccessResponse,
19};
20#[cfg(all(feature = "js", target_arch = "wasm32"))]
21use gloo_utils::format::JsValueSerdeExt;
22use rand::RngExt;
23use serde::{Deserialize, Serialize};
24#[cfg(feature = "js")]
25use wasm_bindgen::prelude::*;
26
27#[derive(Debug, Deserialize, Clone, Serialize)]
29#[cfg_attr(feature = "js", wasm_bindgen)]
30pub struct GetDictionaryItemResult(_GetDictionaryItemResult);
31
32impl From<GetDictionaryItemResult> for _GetDictionaryItemResult {
33 fn from(result: GetDictionaryItemResult) -> Self {
34 result.0
35 }
36}
37impl From<_GetDictionaryItemResult> for GetDictionaryItemResult {
38 fn from(result: _GetDictionaryItemResult) -> Self {
39 GetDictionaryItemResult(result)
40 }
41}
42
43impl GetDictionaryItemResult {
44 pub fn stored_value_typed(&self) -> StoredValue {
46 self.0.stored_value.clone().into()
47 }
48}
49
50#[cfg(all(feature = "js", target_arch = "wasm32"))]
51#[cfg_attr(feature = "js", wasm_bindgen)]
52impl GetDictionaryItemResult {
53 #[cfg_attr(feature = "js", wasm_bindgen(getter))]
55 #[cfg(feature = "js")]
56 pub fn api_version(&self) -> JsValue {
57 JsValue::from_serde(&self.0.api_version).unwrap()
58 }
59
60 #[cfg_attr(feature = "js", wasm_bindgen(getter))]
62 pub fn dictionary_key(&self) -> String {
63 self.0.dictionary_key.clone()
64 }
65
66 #[cfg_attr(feature = "js", wasm_bindgen(getter))]
68 #[cfg(feature = "js")]
69 pub fn stored_value(&self) -> JsValue {
70 JsValue::from_serde(&self.0.stored_value).unwrap()
71 }
72
73 #[cfg_attr(feature = "js", wasm_bindgen(js_name = "storedValueTyped"))]
75 pub fn stored_value_typed_js(&self) -> StoredValue {
76 self.stored_value_typed()
77 }
78
79 #[cfg_attr(feature = "js", wasm_bindgen(getter))]
81 pub fn merkle_proof(&self) -> String {
82 self.0.merkle_proof.clone()
83 }
84
85 #[cfg_attr(feature = "js", wasm_bindgen(js_name = "toJson"))]
87 #[cfg(feature = "js")]
88 pub fn to_json(&self) -> JsValue {
89 JsValue::from_serde(&self.0).unwrap_or(JsValue::null())
90 }
91}
92
93#[derive(Default, Debug, Deserialize, Clone, Serialize)]
95#[cfg(all(feature = "js", target_arch = "wasm32"))]
96#[cfg_attr(
97 feature = "js",
98 wasm_bindgen(js_name = "getDictionaryItemOptions", getter_with_clone)
99)]
100pub struct GetDictionaryItemOptions {
101 pub state_root_hash_as_string: Option<String>,
102 pub state_root_hash: Option<Digest>,
103 pub dictionary_item_params: Option<DictionaryItemStrParams>,
104 pub dictionary_item_identifier: Option<DictionaryItemIdentifier>,
105 pub rpc_address: Option<String>,
106 pub verbosity: Option<Verbosity>,
107}
108
109#[cfg(all(feature = "js", target_arch = "wasm32"))]
110#[cfg_attr(feature = "js", wasm_bindgen)]
111impl SDK {
112 #[cfg(feature = "js")]
122 pub fn get_dictionary_item_options(
123 &self,
124 options: JsValue,
125 ) -> Result<GetDictionaryItemOptions, JsError> {
126 options
127 .into_serde::<GetDictionaryItemOptions>()
128 .map_err(|err| JsError::new(&format!("Error deserializing options: {err:?}")))
129 }
130
131 #[cfg_attr(feature = "js", wasm_bindgen(js_name = "get_dictionary_item"))]
145 #[cfg(feature = "js")]
146 pub async fn get_dictionary_item_js_alias(
147 &self,
148 options: Option<GetDictionaryItemOptions>,
149 ) -> Result<GetDictionaryItemResult, JsError> {
150 let GetDictionaryItemOptions {
151 state_root_hash_as_string,
152 state_root_hash,
153 dictionary_item_params,
154 dictionary_item_identifier,
155 verbosity,
156 rpc_address,
157 } = options.unwrap_or_default();
158
159 let dictionary_item = if let Some(identifier) = dictionary_item_identifier {
160 DictionaryItemInput::Identifier(identifier)
161 } else if let Some(params) = dictionary_item_params {
162 DictionaryItemInput::Params(Box::new(params))
163 } else {
164 let err = "Error: Missing dictionary item identifier or params";
165 return Err(JsError::new(err));
166 };
167
168 let result = if let Some(hash) = state_root_hash {
169 self.get_dictionary_item(dictionary_item, Some(hash), verbosity, rpc_address)
170 .await
171 } else if let Some(hash) = state_root_hash_as_string.clone() {
172 self.get_dictionary_item(dictionary_item, Some(hash.as_str()), verbosity, rpc_address)
173 .await
174 } else {
175 self.get_dictionary_item(dictionary_item, None::<&str>, verbosity, rpc_address)
176 .await
177 };
178
179 match result {
180 Ok(data) => Ok(data.result.into()),
181 Err(err) => {
182 let err = &format!("Error occurred with {err:?}");
183 Err(JsError::new(err))
184 }
185 }
186 }
187
188 #[deprecated(note = "This function is an alias. Please use `get_dictionary_item` instead.")]
190 #[allow(deprecated)]
191 #[cfg(feature = "js")]
192 pub async fn state_get_dictionary_item(
193 &self,
194 options: Option<GetDictionaryItemOptions>,
195 ) -> Result<GetDictionaryItemResult, JsError> {
196 self.get_dictionary_item_js_alias(options).await
197 }
198}
199#[derive(Debug, Clone)]
200pub enum DictionaryItemInput {
201 Identifier(DictionaryItemIdentifier),
202 Params(Box<DictionaryItemStrParams>),
203}
204
205impl SDK {
206 pub async fn get_dictionary_item(
223 &self,
224 dictionary_item_input: DictionaryItemInput,
225 state_root_hash: Option<impl ToDigest>,
226 verbosity: Option<Verbosity>,
227 rpc_address: Option<String>,
228 ) -> Result<SuccessResponse<_GetDictionaryItemResult>, SdkError> {
229 let state_root_hash = if let Some(state_root_hash) = state_root_hash {
232 if state_root_hash.is_empty() {
233 let state_root_hash = self
234 .get_state_root_hash(
235 None,
236 None,
237 Some(self.get_rpc_address(rpc_address.clone())),
238 )
239 .await;
240
241 match state_root_hash {
242 Ok(state_root_hash) => {
243 let state_root_hash: Digest =
244 state_root_hash.result.state_root_hash.unwrap().into();
245 state_root_hash
246 }
247 Err(_) => "".to_digest(),
248 }
249 } else {
250 state_root_hash.to_digest()
251 }
252 } else {
253 let state_root_hash = self
254 .get_state_root_hash(None, None, Some(self.get_rpc_address(rpc_address.clone())))
255 .await;
256
257 match state_root_hash {
258 Ok(state_root_hash) => {
259 let state_root_hash: Digest =
260 state_root_hash.result.state_root_hash.unwrap().into();
261 state_root_hash
262 }
263 Err(_) => "".to_digest(),
264 }
265 };
266 let random_id = rand::rng().random::<u64>().to_string();
267 match dictionary_item_input {
268 DictionaryItemInput::Params(dictionary_item_params) => {
269 let dictionary_item_params =
270 dictionary_item_str_params_to_casper_client(&dictionary_item_params)
271 .map_err(|err| *err)?;
272 get_dictionary_item_cli(
273 &random_id,
274 &self.get_rpc_address(rpc_address),
275 self.get_verbosity(verbosity).into(),
276 &state_root_hash.to_string(),
277 dictionary_item_params,
278 )
279 .await
280 .map_err(SdkError::from)
281 }
282 DictionaryItemInput::Identifier(dictionary_item_identifier) => get_dictionary_item_lib(
283 JsonRpcId::from(random_id),
284 &self.get_rpc_address(rpc_address),
285 self.get_verbosity(verbosity).into(),
286 state_root_hash.into(),
287 dictionary_item_identifier.into(),
288 )
289 .await
290 .map_err(SdkError::from),
291 }
292 }
293}
294
295#[cfg(test)]
296mod tests {
297 use super::*;
298 use crate::get_dictionary_item;
299 use sdk_tests::tests::helpers::get_network_constants;
300
301 #[tokio::test]
302 async fn test_get_dictionary_item_with_none_values() {
303 let sdk = SDK::new(None, None, None);
305 let error_message = "failed to parse node address as valid URL";
306
307 let result = sdk
309 .get_dictionary_item(
310 get_dictionary_item(false).await,
311 Some("7d3dc9c74fe93e83fe6cc7a9830ba223035ad4fd4fd464489640742069ca31ed"), None,
313 None,
314 )
315 .await;
316
317 assert!(result.is_err());
319 let err_string = result.err().unwrap().to_string();
320 assert!(err_string.contains(error_message));
321 }
322
323 #[tokio::test]
324 async fn test_get_dictionary_item_with_state_root_hash() {
325 let sdk = SDK::new(None, None, None);
327 let verbosity = Some(Verbosity::High);
328 let (rpc_address, _, _, _, _) = get_network_constants();
329 let dictionary_item = get_dictionary_item(false).await;
330 let state_root_hash: Digest = sdk
331 .get_state_root_hash(None, verbosity, Some(rpc_address.clone()))
332 .await
333 .unwrap()
334 .result
335 .state_root_hash
336 .unwrap()
337 .into();
338
339 let result = sdk
341 .get_dictionary_item(
342 dictionary_item,
343 Some(state_root_hash),
344 verbosity,
345 Some(rpc_address),
346 )
347 .await;
348
349 assert!(result.is_ok());
351 }
352
353 #[tokio::test]
354 async fn test_get_dictionary_item_with_empty_state_root_hash() {
355 let sdk = SDK::new(None, None, None);
357 let verbosity = Some(Verbosity::High);
358 let (rpc_address, _, _, _, _) = get_network_constants();
359
360 let result = sdk
362 .get_dictionary_item(
363 get_dictionary_item(false).await,
364 None::<&str>,
365 verbosity,
366 Some(rpc_address),
367 )
368 .await;
369
370 assert!(result.is_ok());
372 }
373
374 #[tokio::test]
375 async fn test_get_dictionary_item_with_valid_identifier_input() {
376 let sdk = SDK::new(None, None, None);
378 let verbosity = Some(Verbosity::High);
379 let (rpc_address, _, _, _, _) = get_network_constants();
380
381 let result = sdk
383 .get_dictionary_item(
384 get_dictionary_item(false).await,
385 None::<&str>,
386 verbosity,
387 Some(rpc_address),
388 )
389 .await;
390
391 assert!(result.is_ok());
393 }
394
395 #[tokio::test]
396 async fn test_get_dictionary_item_with_valid_params_input() {
397 let sdk = SDK::new(None, None, None);
399 let verbosity = Some(Verbosity::High);
400 let (rpc_address, _, _, _, _) = get_network_constants();
401
402 let result = sdk
404 .get_dictionary_item(
405 get_dictionary_item(true).await,
406 None::<&str>,
407 verbosity,
408 Some(rpc_address),
409 )
410 .await;
411
412 assert!(result.is_ok());
414 }
415
416 #[tokio::test]
417 async fn test_get_dictionary_item_with_invalid_params_input() {
418 let sdk = SDK::new(None, None, None);
420 let verbosity = Some(Verbosity::High);
421 let (rpc_address, _, _, _, _) = get_network_constants();
422
423 let error_message = "no dictionary item params set";
424
425 let state_root_hash = "";
426 let params = DictionaryItemStrParams::new();
427
428 let result = sdk
430 .get_dictionary_item(
431 DictionaryItemInput::Params(Box::new(params)),
432 Some(state_root_hash),
433 verbosity,
434 Some(rpc_address),
435 )
436 .await;
437
438 assert!(result.is_err());
440 let err_string = result.err().unwrap().to_string();
441 assert!(err_string.contains(error_message));
442 }
443
444 #[tokio::test]
445 async fn test_get_dictionary_item_with_error() {
446 let sdk = SDK::new(Some("http://localhost".to_string()), None, None);
448 let error_message = "error sending request";
449
450 let result = sdk
452 .get_dictionary_item(
453 get_dictionary_item(false).await,
454 Some("7d3dc9c74fe93e83fe6cc7a9830ba223035ad4fd4fd464489640742069ca31ed"), None,
456 None,
457 )
458 .await;
459
460 assert!(result.is_err());
462 let err_string = result.err().unwrap().to_string();
463 assert!(err_string.contains(error_message));
464 }
465}