Skip to main content

casper_rust_wasm_sdk/helpers/
mod.rs

1use crate::types::hash::account_hash::AccountHash;
2use crate::types::transaction_params::transaction_str_params::{DEFAULT_GAS_PRICE, DEFAULT_TTL};
3use crate::types::{key::Key, public_key::PublicKey, sdk_error::SdkError, verbosity::Verbosity};
4use base64::engine::general_purpose;
5use base64::Engine;
6use bigdecimal::BigDecimal;
7use blake2::{
8    digest::{Update, VariableOutput},
9    Blake2bVar,
10};
11use casper_client::cli::JsonArg;
12use casper_types::{
13    bytesrepr::ToBytes, cl_value_to_json as cl_value_to_json_from_casper_types, CLValue,
14    Key as CasperTypesKey, NamedArg, PublicKey as CasperTypesPublicKey, RuntimeArgs, SecretKey,
15    TimeDiff, Timestamp,
16};
17use chrono::{DateTime, SecondsFormat, Utc};
18#[cfg(all(feature = "js", target_arch = "wasm32"))]
19use gloo_utils::format::JsValueSerdeExt;
20use serde::Serialize;
21use serde_json::Value;
22use std::str::FromStr;
23#[cfg(all(feature = "js", target_arch = "wasm32"))]
24use wasm_bindgen::{JsCast, JsValue};
25
26pub const BLAKE2B_DIGEST_LENGTH: usize = 32;
27
28/// Converts a CLValue to a JSON Value.
29///
30/// # Arguments
31///
32/// * `cl_value` - The CLValue to convert.
33///
34/// # Returns
35///
36/// A JSON Value representing the CLValue data.
37pub fn cl_value_to_json(cl_value: &CLValue) -> Option<Value> {
38    cl_value_to_json_from_casper_types(cl_value)
39}
40
41/// Gets the current timestamp.
42///
43/// # Arguments
44///
45/// * `timestamp` - An optional timestamp value in milliseconds since the Unix epoch.
46///
47/// # Returns
48///
49/// A string containing the current timestamp in RFC3339 format.
50pub fn get_current_timestamp(timestamp: Option<String>) -> String {
51    let parsed_timestamp = timestamp.as_ref().and_then(|ts| ts.parse::<i64>().ok());
52    let current_timestamp = parsed_timestamp
53        .map(|parsed_time| DateTime::from_timestamp(parsed_time / 1000, 0).unwrap_or_else(Utc::now))
54        .unwrap_or_else(Utc::now);
55    current_timestamp.to_rfc3339_opts(SecondsFormat::Secs, true)
56}
57
58/// Computes the Blake2b hash of the provided metadata.
59///
60/// # Arguments
61///
62/// * `meta_data` - A reference to a string containing the metadata to be hashed.
63///
64/// # Returns
65///
66/// A hexadecimal string representing the Blake2b hash of the input metadata.
67pub fn get_blake2b_hash(meta_data: &str) -> String {
68    let mut result = [0; BLAKE2B_DIGEST_LENGTH];
69    let mut hasher = Blake2bVar::new(BLAKE2B_DIGEST_LENGTH).expect("should create hasher");
70
71    hasher.update(meta_data.as_bytes());
72    hasher
73        .finalize_variable(&mut result)
74        .expect("finalize failed");
75    hex::encode(result).to_lowercase()
76}
77
78/// Creates a dictionary item key by concatenating the serialized bytes of the key and value.
79///
80/// # Arguments
81///
82/// * `key` - The key to be serialized.
83/// * `value` - The value to be serialized.
84///
85/// # Returns
86///
87/// A hexadecimal-encoded string representing the dictionary item key.
88///
89/// # Panics
90///
91/// Panics if the hasher cannot be created.
92///
93/// # Example
94///
95/// ```rust
96/// use casper_types::{U256, U512};
97/// use casper_rust_wasm_sdk::helpers::make_dictionary_item_key;
98/// use casper_rust_wasm_sdk::types::key::Key;
99/// // Key / Value as U256
100/// let key = Key::from_formatted_str( "account-hash-e11bfffe63bf899ea07117af8a2bb43ef0078c0e38ebee6b6cb0b0e39c233538").unwrap();
101/// let value = U256::from(1);
102/// let dictionary_item_key = make_dictionary_item_key(&key, &value);
103/// println!("Dictionary Item Key (Key/Value as U256): {}", dictionary_item_key);
104/// assert_eq!(dictionary_item_key,"145f6211a24c0a8af16b47e7aa58431ea25172eb402903b3c25ac92b9784c7a9".to_string());
105/// // Key / Value as Key
106/// let key = Key::from_formatted_str( "account-hash-813428ce1a9805f1087db07e6017c6c4f5af0ee78a05591bb6577763e89b4f1f").unwrap();
107/// let value = Key::from_formatted_str("account-hash-e11bfffe63bf899ea07117af8a2bb43ef0078c0e38ebee6b6cb0b0e39c233538").unwrap();
108/// let dictionary_item_key = make_dictionary_item_key(&key, &value);
109/// println!("Dictionary Item Key (Key/Value as Key): {}", dictionary_item_key);
110/// assert_eq!(dictionary_item_key,"1e26dc82db208943c3785c0e11b9d78b9c408fee748c78dda5a5d016840dedca".to_string());
111/// ```
112pub fn make_dictionary_item_key<V: ToBytes>(key: &Key, value: &V) -> String {
113    let key: CasperTypesKey = CasperTypesKey::from(key.clone());
114    let mut bytes_a = key.to_bytes().unwrap_or_default();
115    let mut bytes_b = value.to_bytes().unwrap_or_default();
116
117    bytes_a.append(&mut bytes_b);
118
119    let mut result = [0; BLAKE2B_DIGEST_LENGTH];
120    let mut hasher = Blake2bVar::new(BLAKE2B_DIGEST_LENGTH).expect("should create hasher");
121
122    hasher.update(&bytes_a);
123    hasher
124        .finalize_variable(&mut result)
125        .expect("finalize failed");
126    hex::encode(result)
127}
128
129/// Convert a formatted account hash to a base64-encoded Key as string (cep-18 key encoding).
130///
131/// # Arguments
132///
133/// * `formatted_account_hash` - A hex-formatted string representing the account hash.
134///
135/// Example: "account-hash-b485c074cef7ccaccd0302949d2043ab7133abdb14cfa87e8392945c0bd80a5f"
136///
137/// # Returns
138///
139/// Returns a `Result` with the base64-encoded string on success, or a `SdkError` on failure.
140/// Example: "ALSFwHTO98yszQMClJ0gQ6txM6vbFM+ofoOSlFwL2Apf"
141pub fn get_base64_key_from_account_hash(account_hash: &str) -> Result<String, Box<SdkError>> {
142    let account_hash = AccountHash::from_formatted_str(account_hash)?;
143    let key = Key::from_account(account_hash).to_bytes().unwrap();
144    Ok(general_purpose::STANDARD.encode(key)) // base64.encode
145}
146
147/// Converts a formatted key hash to a base64-encoded string (CEP-18 key encoding).
148///
149/// # Arguments
150///
151/// * `formatted_hash` - A hex-formatted string representing the key hash.
152///   Example: "hash-b485c074cef7ccaccd0302949d2043ab7133abdb14cfa87e8392945c0bd80a5f"
153///
154/// # Returns
155///
156/// Returns a `Result` containing the base64-encoded string on success.
157/// Example: "AbSFwHTO98yszQMClJ0gQ6txM6vbFM+ofoOSlFwL2Apf"
158///
159/// # Errors
160///
161/// This function returns an error if:
162/// - The input string is not a valid formatted key hash.
163/// - The conversion to bytes or base64 encoding fails.
164pub fn get_base64_key_from_key_hash(formatted_hash: &str) -> Result<String, Box<SdkError>> {
165    let key = Key::from_formatted_str(formatted_hash)?;
166    let key = key.to_bytes().unwrap();
167    Ok(general_purpose::STANDARD.encode(key)) // base64.encode
168}
169
170/// Prefix used when addressable entities are enabled for contract-shaped named keys.
171pub const ENTITY_CONTRACT_PREFIX: &str = "entity-contract-";
172/// Prefix expected by `query_global_state` for classic `StoredValue::Contract` keys.
173pub const HASH_PREFIX: &str = "hash-";
174
175/// Maps a contract-shaped formatted key to the `hash-…` form used when AE is off.
176///
177/// With addressable entities disabled, account named keys often still store
178/// `entity-contract-<hex>`, but `query_global_state` and contract-info identifiers
179/// resolve `hash-<hex>` to `StoredValue::Contract`. Pass the named-key string
180/// through this helper before those queries.
181///
182/// - `entity-contract-<hex>` → `hash-<hex>`
183/// - already `hash-…` → unchanged
184/// - bare hex (no prefix) → `hash-<hex>`
185/// - any other prefix → unchanged
186pub fn contract_hash_key_for_global_state(formatted: &str) -> String {
187    if let Some(hex) = formatted.strip_prefix(ENTITY_CONTRACT_PREFIX) {
188        format!("{HASH_PREFIX}{hex}")
189    } else if formatted.starts_with(HASH_PREFIX) {
190        formatted.to_string()
191    } else if formatted.contains('-') {
192        // account-hash-, uref-, package-, addressable-entity-, …
193        formatted.to_string()
194    } else {
195        format!("{HASH_PREFIX}{formatted}")
196    }
197}
198
199/// Strips `hash-` or `entity-contract-` for hex-only comparisons.
200pub fn strip_contract_key_prefix(formatted: &str) -> &str {
201    formatted
202        .strip_prefix(HASH_PREFIX)
203        .or_else(|| formatted.strip_prefix(ENTITY_CONTRACT_PREFIX))
204        .unwrap_or(formatted)
205}
206
207/// Gets the time to live (TTL) value or returns the default value if not provided.
208///
209/// # Arguments
210///
211/// * `ttl` - An optional TTL value as a string.
212///
213/// # Returns
214///
215/// A string containing the TTL value or the default TTL if not provided.
216pub fn get_ttl_or_default(ttl: Option<&str>) -> String {
217    if let Some(ttl) = ttl {
218        ttl.to_string()
219    } else {
220        DEFAULT_TTL.to_string()
221    }
222}
223
224/// Parses a timestamp string into a `Timestamp` object.
225///
226/// # Arguments
227///
228/// * `value` - The timestamp string to parse.
229///
230/// # Returns
231///
232/// A `Result` containing the parsed `Timestamp` or an error if parsing fails.
233pub fn parse_timestamp(value: &str) -> Result<Timestamp, Box<SdkError>> {
234    Timestamp::from_str(value).map_err(|error| {
235        Box::new(SdkError::FailedToParseTimestamp {
236            context: "timestamp",
237            error,
238        })
239    })
240}
241
242/// Parses a TTL (time to live) string into a `TimeDiff` object.
243///
244/// # Arguments
245///
246/// * `value` - The TTL string to parse.
247///
248/// # Returns
249///
250/// A `Result` containing the parsed `TimeDiff` or an error if parsing fails.
251pub fn parse_ttl(value: &str) -> Result<TimeDiff, Box<SdkError>> {
252    TimeDiff::from_str(value).map_err(|error| {
253        Box::new(SdkError::FailedToParseTimeDiff {
254            context: "ttl",
255            error,
256        })
257    })
258}
259
260/// Gets the gas price or returns the default value if not provided.
261///
262/// # Arguments
263///
264/// * `gas_price` - An optional gas price value.
265///
266/// # Returns
267///
268/// The gas price or the default gas price if not provided.
269pub fn get_gas_price_or_default(gas_price: Option<u64>) -> u64 {
270    gas_price.unwrap_or(DEFAULT_GAS_PRICE)
271}
272
273/// Gets the value as a string or returns an empty string if not provided.
274///
275/// # Arguments
276///
277/// * `opt_str` - An optional string value.
278///
279/// # Returns
280///
281/// The string value or an empty string if not provided.
282pub(crate) fn get_str_or_default(opt_str: Option<&String>) -> &str {
283    opt_str.map(String::as_str).unwrap_or_default()
284}
285
286/// Generates a secret key using the Ed25519 algorithm.
287///
288/// # Returns
289///
290/// A `Result` containing the generated secret key or an error if the generation fails.
291///
292/// # Errors
293///
294/// Returns an `SdkError` if the secret key generation fails.
295pub fn secret_key_generate() -> Result<SecretKey, Box<SdkError>> {
296    SecretKey::generate_ed25519().map_err(|err| {
297        Box::new(SdkError::FailedToGenerateSecretKey {
298            context: "secret_key_from_pem".to_string(),
299            error: err,
300        })
301    })
302}
303
304/// Generates a secret key using the secp256k1 algorithm.
305///
306/// # Returns
307///
308/// A `Result` containing the generated secret key or an error if the generation fails.
309///
310/// # Errors
311///
312/// Returns an `SdkError` if the secret key generation fails.
313pub fn secret_key_secp256k1_generate() -> Result<SecretKey, Box<SdkError>> {
314    SecretKey::generate_secp256k1().map_err(|err| {
315        Box::new(SdkError::FailedToGenerateSecretKey {
316            context: "secret_key_from_pem".to_string(),
317            error: err,
318        })
319    })
320}
321
322/// Parses a secret key in PEM format into a `SecretKey` object.
323///
324/// # Arguments
325///
326/// * `secret_key` - The secret key in PEM format.
327///
328/// # Returns
329///
330/// A `Result` containing the parsed `SecretKey` or an error if parsing fails.
331pub fn secret_key_from_pem(secret_key: &str) -> Result<SecretKey, Box<SdkError>> {
332    SecretKey::from_pem(secret_key).map_err(|err| {
333        Box::new(SdkError::FailedToParseSecretKey {
334            context: "secret_key_from_pem".to_string(),
335            error: err,
336        })
337    })
338}
339
340/// Converts a secret key in PEM format to its corresponding public key as a string.
341///
342/// # Arguments
343///
344/// * `secret_key` - The secret key in PEM format.
345///
346/// # Returns
347///
348/// A `Result` containing the public key as a string or an error if the conversion fails.
349pub fn public_key_from_secret_key(secret_key: &str) -> Result<String, Box<SdkError>> {
350    // Handle the secret key parsing and map the error
351    let secret_key_from_pem = secret_key_from_pem(secret_key)?;
352
353    // Convert the secret key to public key and handle potential errors
354    let public_key = CasperTypesPublicKey::from(&secret_key_from_pem);
355
356    // Convert to desired public key format
357    let public_key_test: PublicKey = public_key.into();
358
359    // Return the public key as a string
360    Ok(public_key_test.to_string())
361}
362
363/// Converts a hexadecimal string to a vector of unsigned 8-bit integers (Uint8Array).
364///
365/// # Arguments
366///
367/// * `hex_string` - The hexadecimal string to convert.
368///
369/// # Returns
370///
371/// A vector of unsigned 8-bit integers (Uint8Array) containing the converted value.
372pub fn hex_to_uint8_vec(hex_string: &str) -> Vec<u8> {
373    let mut bytes = Vec::with_capacity(hex_string.len() / 2);
374    let mut hex_chars = hex_string.chars();
375    while let (Some(a), Some(b)) = (hex_chars.next(), hex_chars.next()) {
376        if let Ok(byte) = u8::from_str_radix(&format!("{a}{b}"), 16) {
377            bytes.push(byte);
378        } else {
379            // If an invalid hex pair is encountered, return an empty vector.
380            return Vec::new();
381        }
382    }
383    bytes
384}
385
386/// Converts a hexadecimal string to a regular string.
387///
388/// # Arguments
389///
390/// * `hex_string` - The hexadecimal string to convert.
391///
392/// # Returns
393///
394/// A regular string containing the converted value.
395pub fn hex_to_string(hex_string: &str) -> String {
396    match hex::decode(hex_string) {
397        Ok(bytes) => String::from_utf8_lossy(&bytes).to_string(),
398        Err(_) => hex_string.to_string(),
399    }
400}
401
402/// Converts motes to CSPR (Casper tokens).
403///
404/// # Arguments
405///
406/// * `motes` - The motes value to convert.
407///
408/// # Returns
409///
410/// A string representing the CSPR amount.
411pub fn motes_to_cspr(motes: &str) -> Result<String, Box<SdkError>> {
412    match BigDecimal::from_str(motes) {
413        Ok(motes_decimal) => {
414            let divisor = BigDecimal::from(1_000_000_000);
415            let cspr_decimal = &motes_decimal / divisor;
416            let formatted_cspr = format!("{cspr_decimal:.2}");
417
418            if formatted_cspr.ends_with(".00") {
419                Ok(formatted_cspr.replace(".00", ""))
420            } else {
421                Ok(formatted_cspr)
422            }
423        }
424        Err(err) => Err(Box::new(SdkError::CustomError {
425            context: "Failed to parse input as BigDecimal",
426            error: format!("{err:?}"),
427        })),
428    }
429}
430
431/// Pretty prints a serializable value as a JSON string.
432///
433/// # Arguments
434///
435/// * `value` - The serializable value to pretty print.
436/// * `verbosity` - An optional verbosity level for pretty printing.
437///
438/// # Returns
439///
440/// A JSON string representing the pretty printed value.
441pub fn json_pretty_print<T>(value: T, verbosity: Option<Verbosity>) -> Result<String, Box<SdkError>>
442where
443    T: Serialize,
444{
445    let deserialized = serde_json::to_value(&value).map_err(|e| Box::new(SdkError::from(e)))?;
446
447    match verbosity {
448        Some(Verbosity::Low) | None => Ok(deserialized.to_string()),
449        Some(Verbosity::Medium) => casper_types::json_pretty_print(&deserialized).map_err(|err| {
450            Box::new(SdkError::CustomError {
451                context: "Error in json_pretty_print",
452                error: format!("{err}"),
453            })
454        }),
455        Some(Verbosity::High) => {
456            serde_json::to_string_pretty(&deserialized).map_err(|e| Box::new(SdkError::from(e)))
457        }
458    }
459}
460
461/// Inserts a JavaScript value argument into a RuntimeArgs map.
462///
463/// # Arguments
464///
465/// * `args` - The RuntimeArgs map to insert the argument into.
466/// * `js_value_arg` - The JavaScript value argument to insert.
467///
468/// # Returns
469///
470/// The modified `RuntimeArgs` map.
471#[cfg(all(feature = "js", target_arch = "wasm32"))]
472pub fn insert_js_value_arg(
473    args: &mut RuntimeArgs,
474    js_value_arg: JsValue,
475) -> Result<&RuntimeArgs, SdkError> {
476    if js_value_arg.is_instance_of::<js_sys::Object>() {
477        let json_arg: JsonArg = js_value_arg
478            .into_serde()
479            .map_err(|err| SdkError::CustomError {
480                context: "Error converting to JsonArg",
481                error: format!("{err:?}"),
482            })?;
483
484        let named_arg = NamedArg::try_from(json_arg).map_err(|err| SdkError::CustomError {
485            context: "Error converting to NamedArg",
486            error: format!("{err:?}"),
487        })?;
488
489        args.insert_cl_value(named_arg.name(), named_arg.cl_value().clone());
490    } else if let Some(string_arg) = js_value_arg.as_string() {
491        let simple_arg = string_arg;
492        casper_client::cli::insert_arg(&simple_arg, args).map_err(|err| SdkError::CustomError {
493            context: "Error inserting simple arg",
494            error: format!("{err:?}"),
495        })?;
496    } else {
497        return Err(SdkError::CustomError {
498            context: "Error converting to JsonArg or Simple Arg",
499            error: String::from("Conversion failed"),
500        });
501    }
502
503    Ok(args)
504}
505
506/// Inserts an argument into a RuntimeArgs map.
507///
508/// # Arguments
509///
510/// * `args` - The RuntimeArgs map to insert the argument into.
511/// * `new_arg` - The argument as a string.
512///
513/// # Returns
514///
515/// The modified `RuntimeArgs` map.
516pub(crate) fn insert_arg(args: &mut RuntimeArgs, new_arg: String) -> &RuntimeArgs {
517    match serde_json::from_str::<JsonArg>(&new_arg) {
518        Ok(json_arg) => {
519            if let Ok(named_arg) = NamedArg::try_from(json_arg.clone()) {
520                // JSON args
521                args.insert_cl_value(named_arg.name(), named_arg.cl_value().clone());
522            }
523        }
524        Err(_) => {
525            // Simple args
526            let _ = casper_client::cli::insert_arg(&new_arg, args);
527        }
528    }
529    args
530}
531
532#[cfg(test)]
533mod tests {
534    use super::*;
535    use casper_types::U256;
536
537    #[test]
538    fn test_contract_hash_key_for_global_state() {
539        let hex = "5be5b0ef09a7016e11292848d77f539e55791cb07a7012fbc336b1f92a4fe743";
540        assert_eq!(
541            contract_hash_key_for_global_state(&format!("entity-contract-{hex}")),
542            format!("hash-{hex}")
543        );
544        assert_eq!(
545            contract_hash_key_for_global_state(&format!("hash-{hex}")),
546            format!("hash-{hex}")
547        );
548        assert_eq!(
549            contract_hash_key_for_global_state(hex),
550            format!("hash-{hex}")
551        );
552        assert_eq!(
553            contract_hash_key_for_global_state(&format!("uref-{hex}-007")),
554            format!("uref-{hex}-007")
555        );
556        assert_eq!(
557            strip_contract_key_prefix(&format!("entity-contract-{hex}")),
558            hex
559        );
560        assert_eq!(strip_contract_key_prefix(&format!("hash-{hex}")), hex);
561    }
562
563    #[test]
564    fn test_cl_value_to_json() {
565        let cl_value = CLValue::from_t((1, 2, 3)).unwrap();
566        let json_value = cl_value_to_json(&cl_value).unwrap();
567        assert_eq!(
568            json_value,
569            Value::Array(vec![
570                Value::Number(1.into()),
571                Value::Number(2.into()),
572                Value::Number(3.into())
573            ])
574        );
575    }
576
577    #[test]
578    fn test_get_current_timestamp() {
579        let timestamp = Some("1234567890".to_string());
580        let current_timestamp = get_current_timestamp(timestamp);
581        assert_eq!(&current_timestamp, "1970-01-15T06:56:07Z");
582    }
583
584    #[test]
585    fn test_get_blake2b_hash() {
586        let metadata = "some metadata";
587        let hash = get_blake2b_hash(metadata);
588        assert_eq!(
589            &hash,
590            "767de9efccc76bc0eef85ea81fcaa56dc7047e660c74b3dc39f84ab8c4931c0d"
591        );
592    }
593
594    #[test]
595    fn test_get_ttl_or_default() {
596        let ttl = Some("1h".to_string());
597        let ttl_value = get_ttl_or_default(ttl.as_deref());
598        assert_eq!(ttl_value, "1h".to_string());
599
600        let default_ttl = get_ttl_or_default(None);
601        assert_eq!(default_ttl, DEFAULT_TTL.to_string());
602    }
603
604    #[test]
605    fn test_parse_timestamp() {
606        let valid_timestamp = "2023-11-06T12:00:00Z";
607        let parsed_timestamp = parse_timestamp(valid_timestamp);
608        assert!(parsed_timestamp.is_ok());
609
610        let invalid_timestamp = "invalid_timestamp";
611        let parsed_timestamp = parse_timestamp(invalid_timestamp);
612        assert!(parsed_timestamp.is_err());
613    }
614
615    #[test]
616    fn test_parse_ttl() {
617        let valid_ttl = "1h";
618        let parsed_ttl = parse_ttl(valid_ttl);
619        assert!(parsed_ttl.is_ok());
620
621        let invalid_ttl = "invalid_ttl";
622        let parsed_ttl = parse_ttl(invalid_ttl);
623        assert!(parsed_ttl.is_err());
624    }
625
626    #[test]
627    fn test_get_gas_price_or_default() {
628        let gas_price = Some(100);
629        let price = get_gas_price_or_default(gas_price);
630        assert_eq!(price, 100);
631
632        let default_price = get_gas_price_or_default(None);
633        assert_eq!(default_price, DEFAULT_GAS_PRICE);
634    }
635
636    #[test]
637    fn test_get_str_or_default() {
638        let input_str = Some("test_string".to_string());
639        let result = get_str_or_default(input_str.as_ref());
640        assert_eq!(result, "test_string");
641
642        let default_str: Option<String> = None;
643        let result = get_str_or_default(default_str.as_ref());
644        assert_eq!(result, "");
645    }
646
647    #[test]
648    fn test_secret_key_generate() {
649        // Act
650        let result = secret_key_generate();
651
652        // Assert
653        assert!(result.is_ok());
654        let secret_key = result.unwrap();
655        assert_eq!(&secret_key.to_string(), "SecretKey::Ed25519");
656    }
657
658    #[test]
659    fn test_secret_key_secp256k1_generate() {
660        // Act
661        let result = secret_key_secp256k1_generate();
662
663        // Assert
664        assert!(result.is_ok());
665        let secret_key = result.unwrap();
666        assert_eq!(&secret_key.to_string(), "SecretKey::Secp256k1");
667    }
668
669    #[test]
670    fn test_secret_key_from_pem() {
671        let pem_key = "-----BEGIN PRIVATE KEY-----\nTEST\n-----END PRIVATE KEY-----";
672        let result = secret_key_from_pem(pem_key);
673        assert!(result.is_err());
674        let pem_key =
675        "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEII8ULlk1CJ12ZQ+bScjBt/IxMAZNggClWqK56D1/7CbI\n-----END PRIVATE KEY-----";
676        let result = secret_key_from_pem(pem_key);
677        assert!(result.is_ok());
678        assert_eq!(&result.unwrap().to_string(), "SecretKey::Ed25519");
679    }
680
681    #[test]
682    fn test_public_key_from_secret_key() {
683        let pem_key = "-----BEGIN PRIVATE KEY-----\nTEST\n-----END PRIVATE KEY-----";
684        let result = public_key_from_secret_key(pem_key);
685        assert!(result.is_err());
686        let pem_key =
687        "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEII8ULlk1CJ12ZQ+bScjBt/IxMAZNggClWqK56D1/7CbI-----END PRIVATE KEY-----";
688        let result = public_key_from_secret_key(pem_key);
689        assert!(result.is_ok());
690        assert_eq!(
691            result.unwrap(),
692            "01aff5c18a954604dd27d139d8e0cfc533ac3d53784d76c7a7ac5ff4039510fdf6"
693        );
694    }
695
696    #[test]
697    fn test_hex_to_uint8_vec() {
698        let hex_string = "0161e40005434ba3cd9a791a2827f5fa3ee514d1475fe72b2823cbaac9c3c71483";
699        let result = hex_to_uint8_vec(hex_string);
700        assert_eq!(
701            result,
702            vec![
703                1, 97, 228, 0, 5, 67, 75, 163, 205, 154, 121, 26, 40, 39, 245, 250, 62, 229, 20,
704                209, 71, 95, 231, 43, 40, 35, 203, 170, 201, 195, 199, 20, 131
705            ]
706        );
707    }
708
709    #[test]
710    fn test_hex_to_string() {
711        let hex_string = "48656c6c6f20436173706572";
712        let result = hex_to_string(hex_string);
713        assert_eq!(result, "Hello Casper");
714    }
715
716    #[test]
717    fn test_motes_to_cspr() {
718        let motes = "1000000000";
719        let result = motes_to_cspr(motes).unwrap();
720        assert_eq!(result, "1");
721    }
722
723    #[test]
724    fn test_json_pretty_print() {
725        #[derive(Serialize, Clone)]
726        struct TestData {
727            age: i32,
728            name: String,
729        }
730
731        let data = TestData {
732            age: 42,
733            name: "Joe".to_string(),
734        };
735
736        let result = json_pretty_print(data.clone(), None).unwrap();
737        assert_eq!(result, "{\"age\":42,\"name\":\"Joe\"}");
738
739        let result = json_pretty_print(data.clone(), Some(Verbosity::Low)).unwrap();
740        assert_eq!(result, "{\"age\":42,\"name\":\"Joe\"}");
741
742        let result = json_pretty_print(data.clone(), Some(Verbosity::Medium)).unwrap();
743        assert_eq!(result, "{\n  \"age\": 42,\n  \"name\": \"Joe\"\n}");
744
745        let result = json_pretty_print(data, Some(Verbosity::High)).unwrap();
746        assert_eq!(result, "{\n  \"age\": 42,\n  \"name\": \"Joe\"\n}");
747    }
748
749    #[test]
750    fn test_insert_arg_simple() {
751        let mut args = RuntimeArgs::new();
752        let new_arg = "message:String='Hello Casper";
753        let result_args = insert_arg(&mut args, new_arg.to_string());
754        assert_eq!(result_args.len(), 1);
755        let cl_value = result_args.get("message").unwrap();
756        let json = cl_value_to_json(cl_value).unwrap();
757        let expexted_json = Value::String("Hello Casper".to_string());
758        assert_eq!(json, expexted_json);
759    }
760
761    #[test]
762    fn test_insert_arg_json() {
763        let mut args = RuntimeArgs::new();
764        let arg_json = r#"{"name": "bar", "type": "U256", "value": 1}"#; // No brackets only one arg
765        let result_args = insert_arg(&mut args, arg_json.to_string());
766        assert_eq!(result_args.len(), 1);
767        let cl_value = result_args.get("bar").unwrap();
768        let json = cl_value_to_json(cl_value).unwrap();
769        let expexted_json = Value::String("1".to_string());
770        assert_eq!(json, expexted_json);
771    }
772
773    #[test]
774    pub fn test_make_dictionary_item_key() {
775        let key = Key::from_formatted_str(
776            "account-hash-e11bfffe63bf899ea07117af8a2bb43ef0078c0e38ebee6b6cb0b0e39c233538",
777        )
778        .unwrap();
779        let value = U256::from(1);
780        let dictionary_item_key = make_dictionary_item_key(&key, &value);
781        assert_eq!(
782            dictionary_item_key,
783            "145f6211a24c0a8af16b47e7aa58431ea25172eb402903b3c25ac92b9784c7a9".to_string()
784        );
785        let key = Key::from_formatted_str(
786            "account-hash-813428ce1a9805f1087db07e6017c6c4f5af0ee78a05591bb6577763e89b4f1f",
787        )
788        .unwrap();
789        let value = Key::from_formatted_str(
790            "account-hash-e11bfffe63bf899ea07117af8a2bb43ef0078c0e38ebee6b6cb0b0e39c233538",
791        )
792        .unwrap();
793        let dictionary_item_key = make_dictionary_item_key(&key, &value);
794        assert_eq!(
795            dictionary_item_key,
796            "1e26dc82db208943c3785c0e11b9d78b9c408fee748c78dda5a5d016840dedca".to_string()
797        );
798    }
799
800    #[test]
801    fn test_get_base64_key_from_account_hash() {
802        // Test with a known input and expected output
803        let input_hash =
804            "account-hash-b485c074cef7ccaccd0302949d2043ab7133abdb14cfa87e8392945c0bd80a5f";
805        let expected_output = "ALSFwHTO98yszQMClJ0gQ6txM6vbFM+ofoOSlFwL2Apf";
806
807        // Call the function under test
808        let result = get_base64_key_from_account_hash(input_hash).unwrap();
809
810        // Check the result against the expected output
811        assert_eq!(result, expected_output.to_string());
812    }
813
814    #[test]
815    fn test_get_base64_key_from_key_hash() {
816        // Test with a known input and expected output
817        let input_hash = "hash-b485c074cef7ccaccd0302949d2043ab7133abdb14cfa87e8392945c0bd80a5f";
818        let expected_output = "AbSFwHTO98yszQMClJ0gQ6txM6vbFM+ofoOSlFwL2Apf";
819
820        // Call the function under test
821        let result = get_base64_key_from_key_hash(input_hash).unwrap();
822
823        // Check the result against the expected output
824        assert_eq!(result, expected_output.to_string());
825    }
826}