casper_rust_wasm_sdk/helpers/
mod.rs1use 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 VarBlake2b,
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(target_arch = "wasm32")]
19use gloo_utils::format::JsValueSerdeExt;
20use serde::Serialize;
21use serde_json::Value;
22use std::str::FromStr;
23#[cfg(target_arch = "wasm32")]
24use wasm_bindgen::{JsCast, JsValue};
25
26pub const BLAKE2B_DIGEST_LENGTH: usize = 32;
27
28pub fn cl_value_to_json(cl_value: &CLValue) -> Option<Value> {
38 cl_value_to_json_from_casper_types(cl_value)
39}
40
41pub 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
58pub fn get_blake2b_hash(meta_data: &str) -> String {
68 let mut result = [0; BLAKE2B_DIGEST_LENGTH];
69 let mut hasher = VarBlake2b::new(BLAKE2B_DIGEST_LENGTH).expect("should create hasher");
70
71 hasher.update(meta_data);
72 hasher.finalize_variable(|slice| {
73 result.copy_from_slice(slice);
74 });
75 hex::encode(result).to_lowercase()
76}
77
78pub 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 = VarBlake2b::new(BLAKE2B_DIGEST_LENGTH).expect("should create hasher");
121
122 hasher.update(bytes_a);
123 hasher.finalize_variable(|slice| {
124 result.copy_from_slice(slice);
125 });
126 hex::encode(result)
127}
128
129pub 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)) }
146
147pub 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)) }
169
170pub fn get_ttl_or_default(ttl: Option<&str>) -> String {
180 if let Some(ttl) = ttl {
181 ttl.to_string()
182 } else {
183 DEFAULT_TTL.to_string()
184 }
185}
186
187pub fn parse_timestamp(value: &str) -> Result<Timestamp, Box<SdkError>> {
197 Timestamp::from_str(value).map_err(|error| {
198 Box::new(SdkError::FailedToParseTimestamp {
199 context: "timestamp",
200 error,
201 })
202 })
203}
204
205pub fn parse_ttl(value: &str) -> Result<TimeDiff, Box<SdkError>> {
215 TimeDiff::from_str(value).map_err(|error| {
216 Box::new(SdkError::FailedToParseTimeDiff {
217 context: "ttl",
218 error,
219 })
220 })
221}
222
223pub fn get_gas_price_or_default(gas_price: Option<u64>) -> u64 {
233 gas_price.unwrap_or(DEFAULT_GAS_PRICE)
234}
235
236pub(crate) fn get_str_or_default(opt_str: Option<&String>) -> &str {
246 opt_str.map(String::as_str).unwrap_or_default()
247}
248
249pub fn secret_key_generate() -> Result<SecretKey, Box<SdkError>> {
259 SecretKey::generate_ed25519().map_err(|err| {
260 Box::new(SdkError::FailedToGenerateSecretKey {
261 context: "secret_key_from_pem".to_string(),
262 error: err,
263 })
264 })
265}
266
267pub fn secret_key_secp256k1_generate() -> Result<SecretKey, Box<SdkError>> {
277 SecretKey::generate_secp256k1().map_err(|err| {
278 Box::new(SdkError::FailedToGenerateSecretKey {
279 context: "secret_key_from_pem".to_string(),
280 error: err,
281 })
282 })
283}
284
285pub fn secret_key_from_pem(secret_key: &str) -> Result<SecretKey, Box<SdkError>> {
295 SecretKey::from_pem(secret_key).map_err(|err| {
296 Box::new(SdkError::FailedToParseSecretKey {
297 context: "secret_key_from_pem".to_string(),
298 error: err,
299 })
300 })
301}
302
303pub fn public_key_from_secret_key(secret_key: &str) -> Result<String, Box<SdkError>> {
313 let secret_key_from_pem = secret_key_from_pem(secret_key)?;
315
316 let public_key = CasperTypesPublicKey::from(&secret_key_from_pem);
318
319 let public_key_test: PublicKey = public_key.into();
321
322 Ok(public_key_test.to_string())
324}
325
326pub fn hex_to_uint8_vec(hex_string: &str) -> Vec<u8> {
336 let mut bytes = Vec::with_capacity(hex_string.len() / 2);
337 let mut hex_chars = hex_string.chars();
338 while let (Some(a), Some(b)) = (hex_chars.next(), hex_chars.next()) {
339 if let Ok(byte) = u8::from_str_radix(&format!("{a}{b}"), 16) {
340 bytes.push(byte);
341 } else {
342 return Vec::new();
344 }
345 }
346 bytes
347}
348
349pub fn hex_to_string(hex_string: &str) -> String {
359 match hex::decode(hex_string) {
360 Ok(bytes) => String::from_utf8_lossy(&bytes).to_string(),
361 Err(_) => hex_string.to_string(),
362 }
363}
364
365pub fn motes_to_cspr(motes: &str) -> Result<String, Box<SdkError>> {
375 match BigDecimal::from_str(motes) {
376 Ok(motes_decimal) => {
377 let divisor = BigDecimal::from(1_000_000_000);
378 let cspr_decimal = &motes_decimal / divisor;
379 let formatted_cspr = format!("{cspr_decimal:.2}");
380
381 if formatted_cspr.ends_with(".00") {
382 Ok(formatted_cspr.replace(".00", ""))
383 } else {
384 Ok(formatted_cspr)
385 }
386 }
387 Err(err) => Err(Box::new(SdkError::CustomError {
388 context: "Failed to parse input as BigDecimal",
389 error: format!("{err:?}"),
390 })),
391 }
392}
393
394pub fn json_pretty_print<T>(value: T, verbosity: Option<Verbosity>) -> Result<String, Box<SdkError>>
405where
406 T: Serialize,
407{
408 let deserialized = serde_json::to_value(&value).map_err(|e| Box::new(SdkError::from(e)))?;
409
410 match verbosity {
411 Some(Verbosity::Low) | None => Ok(deserialized.to_string()),
412 Some(Verbosity::Medium) => casper_types::json_pretty_print(&deserialized).map_err(|err| {
413 Box::new(SdkError::CustomError {
414 context: "Error in json_pretty_print",
415 error: format!("{err}"),
416 })
417 }),
418 Some(Verbosity::High) => {
419 serde_json::to_string_pretty(&deserialized).map_err(|e| Box::new(SdkError::from(e)))
420 }
421 }
422}
423
424#[cfg(target_arch = "wasm32")]
435pub fn insert_js_value_arg(
436 args: &mut RuntimeArgs,
437 js_value_arg: JsValue,
438) -> Result<&RuntimeArgs, SdkError> {
439 if js_sys::Object::instanceof(&js_value_arg) {
440 let json_arg: JsonArg = js_value_arg
441 .into_serde()
442 .map_err(|err| SdkError::CustomError {
443 context: "Error converting to JsonArg",
444 error: format!("{err:?}"),
445 })?;
446
447 let named_arg = NamedArg::try_from(json_arg).map_err(|err| SdkError::CustomError {
448 context: "Error converting to NamedArg",
449 error: format!("{err:?}"),
450 })?;
451
452 args.insert_cl_value(named_arg.name(), named_arg.cl_value().clone());
453 } else if let Some(string_arg) = js_value_arg.as_string() {
454 let simple_arg = string_arg;
455 casper_client::cli::insert_arg(&simple_arg, args).map_err(|err| SdkError::CustomError {
456 context: "Error inserting simple arg",
457 error: format!("{err:?}"),
458 })?;
459 } else {
460 return Err(SdkError::CustomError {
461 context: "Error converting to JsonArg or Simple Arg",
462 error: String::from("Conversion failed"),
463 });
464 }
465
466 Ok(args)
467}
468
469pub(crate) fn insert_arg(args: &mut RuntimeArgs, new_arg: String) -> &RuntimeArgs {
480 match serde_json::from_str::<JsonArg>(&new_arg) {
481 Ok(json_arg) => {
482 if let Ok(named_arg) = NamedArg::try_from(json_arg.clone()) {
483 args.insert_cl_value(named_arg.name(), named_arg.cl_value().clone());
485 }
486 }
487 Err(_) => {
488 let _ = casper_client::cli::insert_arg(&new_arg, args);
490 }
491 }
492 args
493}
494
495#[cfg(test)]
496mod tests {
497 use super::*;
498 use casper_types::U256;
499
500 #[test]
501 fn test_cl_value_to_json() {
502 let cl_value = CLValue::from_t((1, 2, 3)).unwrap();
503 let json_value = cl_value_to_json(&cl_value).unwrap();
504 assert_eq!(
505 json_value,
506 Value::Array(vec![
507 Value::Number(1.into()),
508 Value::Number(2.into()),
509 Value::Number(3.into())
510 ])
511 );
512 }
513
514 #[test]
515 fn test_get_current_timestamp() {
516 let timestamp = Some("1234567890".to_string());
517 let current_timestamp = get_current_timestamp(timestamp);
518 assert_eq!(¤t_timestamp, "1970-01-15T06:56:07Z");
519 }
520
521 #[test]
522 fn test_get_blake2b_hash() {
523 let metadata = "some metadata";
524 let hash = get_blake2b_hash(metadata);
525 assert_eq!(
526 &hash,
527 "767de9efccc76bc0eef85ea81fcaa56dc7047e660c74b3dc39f84ab8c4931c0d"
528 );
529 }
530
531 #[test]
532 fn test_get_ttl_or_default() {
533 let ttl = Some("1h".to_string());
534 let ttl_value = get_ttl_or_default(ttl.as_deref());
535 assert_eq!(ttl_value, "1h".to_string());
536
537 let default_ttl = get_ttl_or_default(None);
538 assert_eq!(default_ttl, DEFAULT_TTL.to_string());
539 }
540
541 #[test]
542 fn test_parse_timestamp() {
543 let valid_timestamp = "2023-11-06T12:00:00Z";
544 let parsed_timestamp = parse_timestamp(valid_timestamp);
545 assert!(parsed_timestamp.is_ok());
546
547 let invalid_timestamp = "invalid_timestamp";
548 let parsed_timestamp = parse_timestamp(invalid_timestamp);
549 assert!(parsed_timestamp.is_err());
550 }
551
552 #[test]
553 fn test_parse_ttl() {
554 let valid_ttl = "1h";
555 let parsed_ttl = parse_ttl(valid_ttl);
556 assert!(parsed_ttl.is_ok());
557
558 let invalid_ttl = "invalid_ttl";
559 let parsed_ttl = parse_ttl(invalid_ttl);
560 assert!(parsed_ttl.is_err());
561 }
562
563 #[test]
564 fn test_get_gas_price_or_default() {
565 let gas_price = Some(100);
566 let price = get_gas_price_or_default(gas_price);
567 assert_eq!(price, 100);
568
569 let default_price = get_gas_price_or_default(None);
570 assert_eq!(default_price, DEFAULT_GAS_PRICE);
571 }
572
573 #[test]
574 fn test_get_str_or_default() {
575 let input_str = Some("test_string".to_string());
576 let result = get_str_or_default(input_str.as_ref());
577 assert_eq!(result, "test_string");
578
579 let default_str: Option<String> = None;
580 let result = get_str_or_default(default_str.as_ref());
581 assert_eq!(result, "");
582 }
583
584 #[test]
585 fn test_secret_key_generate() {
586 let result = secret_key_generate();
588
589 assert!(result.is_ok());
591 let secret_key = result.unwrap();
592 assert_eq!(&secret_key.to_string(), "SecretKey::Ed25519");
593 }
594
595 #[test]
596 fn test_secret_key_secp256k1_generate() {
597 let result = secret_key_secp256k1_generate();
599
600 assert!(result.is_ok());
602 let secret_key = result.unwrap();
603 assert_eq!(&secret_key.to_string(), "SecretKey::Secp256k1");
604 }
605
606 #[test]
607 fn test_secret_key_from_pem() {
608 let pem_key = "-----BEGIN PRIVATE KEY-----\nTEST\n-----END PRIVATE KEY-----";
609 let result = secret_key_from_pem(pem_key);
610 assert!(result.is_err());
611 let pem_key =
612 "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEII8ULlk1CJ12ZQ+bScjBt/IxMAZNggClWqK56D1/7CbI\n-----END PRIVATE KEY-----";
613 let result = secret_key_from_pem(pem_key);
614 assert!(result.is_ok());
615 assert_eq!(&result.unwrap().to_string(), "SecretKey::Ed25519");
616 }
617
618 #[test]
619 fn test_public_key_from_secret_key() {
620 let pem_key = "-----BEGIN PRIVATE KEY-----\nTEST\n-----END PRIVATE KEY-----";
621 let result = public_key_from_secret_key(pem_key);
622 assert!(result.is_err());
623 let pem_key =
624 "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEII8ULlk1CJ12ZQ+bScjBt/IxMAZNggClWqK56D1/7CbI-----END PRIVATE KEY-----";
625 let result = public_key_from_secret_key(pem_key);
626 assert!(result.is_ok());
627 assert_eq!(
628 result.unwrap(),
629 "01aff5c18a954604dd27d139d8e0cfc533ac3d53784d76c7a7ac5ff4039510fdf6"
630 );
631 }
632
633 #[test]
634 fn test_hex_to_uint8_vec() {
635 let hex_string = "0161e40005434ba3cd9a791a2827f5fa3ee514d1475fe72b2823cbaac9c3c71483";
636 let result = hex_to_uint8_vec(hex_string);
637 assert_eq!(
638 result,
639 vec![
640 1, 97, 228, 0, 5, 67, 75, 163, 205, 154, 121, 26, 40, 39, 245, 250, 62, 229, 20,
641 209, 71, 95, 231, 43, 40, 35, 203, 170, 201, 195, 199, 20, 131
642 ]
643 );
644 }
645
646 #[test]
647 fn test_hex_to_string() {
648 let hex_string = "48656c6c6f20436173706572";
649 let result = hex_to_string(hex_string);
650 assert_eq!(result, "Hello Casper");
651 }
652
653 #[test]
654 fn test_motes_to_cspr() {
655 let motes = "1000000000";
656 let result = motes_to_cspr(motes).unwrap();
657 assert_eq!(result, "1");
658 }
659
660 #[test]
661 fn test_json_pretty_print() {
662 #[derive(Serialize, Clone)]
663 struct TestData {
664 age: i32,
665 name: String,
666 }
667
668 let data = TestData {
669 age: 42,
670 name: "Joe".to_string(),
671 };
672
673 let result = json_pretty_print(data.clone(), None).unwrap();
674 assert_eq!(result, "{\"age\":42,\"name\":\"Joe\"}");
675
676 let result = json_pretty_print(data.clone(), Some(Verbosity::Low)).unwrap();
677 assert_eq!(result, "{\"age\":42,\"name\":\"Joe\"}");
678
679 let result = json_pretty_print(data.clone(), Some(Verbosity::Medium)).unwrap();
680 assert_eq!(result, "{\n \"age\": 42,\n \"name\": \"Joe\"\n}");
681
682 let result = json_pretty_print(data, Some(Verbosity::High)).unwrap();
683 assert_eq!(result, "{\n \"age\": 42,\n \"name\": \"Joe\"\n}");
684 }
685
686 #[test]
687 fn test_insert_arg_simple() {
688 let mut args = RuntimeArgs::new();
689 let new_arg = "message:String='Hello Casper";
690 let result_args = insert_arg(&mut args, new_arg.to_string());
691 assert_eq!(result_args.len(), 1);
692 let cl_value = result_args.get("message").unwrap();
693 let json = cl_value_to_json(cl_value).unwrap();
694 let expexted_json = Value::String("Hello Casper".to_string());
695 assert_eq!(json, expexted_json);
696 }
697
698 #[test]
699 fn test_insert_arg_json() {
700 let mut args = RuntimeArgs::new();
701 let arg_json = r#"{"name": "bar", "type": "U256", "value": 1}"#; let result_args = insert_arg(&mut args, arg_json.to_string());
703 assert_eq!(result_args.len(), 1);
704 let cl_value = result_args.get("bar").unwrap();
705 let json = cl_value_to_json(cl_value).unwrap();
706 let expexted_json = Value::String("1".to_string());
707 assert_eq!(json, expexted_json);
708 }
709
710 #[test]
711 pub fn test_make_dictionary_item_key() {
712 let key = Key::from_formatted_str(
713 "account-hash-e11bfffe63bf899ea07117af8a2bb43ef0078c0e38ebee6b6cb0b0e39c233538",
714 )
715 .unwrap();
716 let value = U256::from(1);
717 let dictionary_item_key = make_dictionary_item_key(&key, &value);
718 assert_eq!(
719 dictionary_item_key,
720 "145f6211a24c0a8af16b47e7aa58431ea25172eb402903b3c25ac92b9784c7a9".to_string()
721 );
722 let key = Key::from_formatted_str(
723 "account-hash-813428ce1a9805f1087db07e6017c6c4f5af0ee78a05591bb6577763e89b4f1f",
724 )
725 .unwrap();
726 let value = Key::from_formatted_str(
727 "account-hash-e11bfffe63bf899ea07117af8a2bb43ef0078c0e38ebee6b6cb0b0e39c233538",
728 )
729 .unwrap();
730 let dictionary_item_key = make_dictionary_item_key(&key, &value);
731 assert_eq!(
732 dictionary_item_key,
733 "1e26dc82db208943c3785c0e11b9d78b9c408fee748c78dda5a5d016840dedca".to_string()
734 );
735 }
736
737 #[test]
738 fn test_get_base64_key_from_account_hash() {
739 let input_hash =
741 "account-hash-b485c074cef7ccaccd0302949d2043ab7133abdb14cfa87e8392945c0bd80a5f";
742 let expected_output = "ALSFwHTO98yszQMClJ0gQ6txM6vbFM+ofoOSlFwL2Apf";
743
744 let result = get_base64_key_from_account_hash(input_hash).unwrap();
746
747 assert_eq!(result, expected_output.to_string());
749 }
750
751 #[test]
752 fn test_get_base64_key_from_key_hash() {
753 let input_hash = "hash-b485c074cef7ccaccd0302949d2043ab7133abdb14cfa87e8392945c0bd80a5f";
755 let expected_output = "AbSFwHTO98yszQMClJ0gQ6txM6vbFM+ofoOSlFwL2Apf";
756
757 let result = get_base64_key_from_key_hash(input_hash).unwrap();
759
760 assert_eq!(result, expected_output.to_string());
762 }
763}