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 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, 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, SdkError> {
197 Timestamp::from_str(value).map_err(|error| SdkError::FailedToParseTimestamp {
198 context: "timestamp",
199 error,
200 })
201}
202
203pub fn parse_ttl(value: &str) -> Result<TimeDiff, SdkError> {
213 TimeDiff::from_str(value).map_err(|error| SdkError::FailedToParseTimeDiff {
214 context: "ttl",
215 error,
216 })
217}
218
219pub fn get_gas_price_or_default(gas_price: Option<u64>) -> u64 {
229 gas_price.unwrap_or(DEFAULT_GAS_PRICE)
230}
231
232pub(crate) fn get_str_or_default(opt_str: Option<&String>) -> &str {
242 opt_str.map(String::as_str).unwrap_or_default()
243}
244
245pub fn secret_key_generate() -> Result<SecretKey, SdkError> {
255 SecretKey::generate_ed25519().map_err(|err| SdkError::FailedToGenerateSecretKey {
256 context: "secret_key_from_pem".to_string(),
257 error: err,
258 })
259}
260
261pub fn secret_key_secp256k1_generate() -> Result<SecretKey, SdkError> {
271 SecretKey::generate_secp256k1().map_err(|err| SdkError::FailedToGenerateSecretKey {
272 context: "secret_key_from_pem".to_string(),
273 error: err,
274 })
275}
276
277pub fn secret_key_from_pem(secret_key: &str) -> Result<SecretKey, SdkError> {
287 SecretKey::from_pem(secret_key).map_err(|err| SdkError::FailedToParseSecretKey {
288 context: "secret_key_from_pem".to_string(),
289 error: err,
290 })
291}
292
293pub fn public_key_from_secret_key(secret_key: &str) -> Result<String, SdkError> {
303 let secret_key_from_pem = secret_key_from_pem(secret_key)?;
305
306 let public_key = CasperTypesPublicKey::from(&secret_key_from_pem);
308
309 let public_key_test: PublicKey = public_key.into();
311
312 Ok(public_key_test.to_string())
314}
315
316pub fn hex_to_uint8_vec(hex_string: &str) -> Vec<u8> {
326 let mut bytes = Vec::with_capacity(hex_string.len() / 2);
327 let mut hex_chars = hex_string.chars();
328 while let (Some(a), Some(b)) = (hex_chars.next(), hex_chars.next()) {
329 if let Ok(byte) = u8::from_str_radix(&format!("{}{}", a, b), 16) {
330 bytes.push(byte);
331 } else {
332 return Vec::new();
334 }
335 }
336 bytes
337}
338
339pub fn hex_to_string(hex_string: &str) -> String {
349 match hex::decode(hex_string) {
350 Ok(bytes) => String::from_utf8_lossy(&bytes).to_string(),
351 Err(_) => hex_string.to_string(),
352 }
353}
354
355pub fn motes_to_cspr(motes: &str) -> Result<String, SdkError> {
365 match BigDecimal::from_str(motes) {
366 Ok(motes_decimal) => {
367 let divisor = BigDecimal::from(1_000_000_000);
368 let cspr_decimal = &motes_decimal / divisor;
369 let formatted_cspr = format!("{:.2}", cspr_decimal);
370
371 if formatted_cspr.ends_with(".00") {
372 Ok(formatted_cspr.replace(".00", ""))
373 } else {
374 Ok(formatted_cspr)
375 }
376 }
377 Err(err) => Err(SdkError::CustomError {
378 context: "Failed to parse input as BigDecimal",
379 error: format!("{:?}", err),
380 }),
381 }
382}
383
384pub fn json_pretty_print<T>(value: T, verbosity: Option<Verbosity>) -> Result<String, SdkError>
395where
396 T: Serialize,
397{
398 let deserialized = serde_json::to_value(&value).map_err(SdkError::from)?;
399
400 match verbosity {
401 Some(Verbosity::Low) | None => Ok(deserialized.to_string()),
402 Some(Verbosity::Medium) => {
403 casper_types::json_pretty_print(&deserialized).map_err(|err| SdkError::CustomError {
404 context: "Error in json_pretty_print",
405 error: format!("{}", err),
406 })
407 }
408 Some(Verbosity::High) => {
409 serde_json::to_string_pretty(&deserialized).map_err(SdkError::from)
410 }
411 }
412}
413
414#[cfg(target_arch = "wasm32")]
425pub fn insert_js_value_arg(
426 args: &mut RuntimeArgs,
427 js_value_arg: JsValue,
428) -> Result<&RuntimeArgs, SdkError> {
429 if js_sys::Object::instanceof(&js_value_arg) {
430 let json_arg: JsonArg = js_value_arg
431 .into_serde()
432 .map_err(|err| SdkError::CustomError {
433 context: "Error converting to JsonArg",
434 error: format!("{:?}", err),
435 })?;
436
437 let named_arg = NamedArg::try_from(json_arg).map_err(|err| SdkError::CustomError {
438 context: "Error converting to NamedArg",
439 error: format!("{:?}", err),
440 })?;
441
442 args.insert_cl_value(named_arg.name(), named_arg.cl_value().clone());
443 } else if let Some(string_arg) = js_value_arg.as_string() {
444 let simple_arg = string_arg;
445 casper_client::cli::insert_arg(&simple_arg, args).map_err(|err| SdkError::CustomError {
446 context: "Error inserting simple arg",
447 error: format!("{:?}", err),
448 })?;
449 } else {
450 return Err(SdkError::CustomError {
451 context: "Error converting to JsonArg or Simple Arg",
452 error: String::from("Conversion failed"),
453 });
454 }
455
456 Ok(args)
457}
458
459pub(crate) fn insert_arg(args: &mut RuntimeArgs, new_arg: String) -> &RuntimeArgs {
470 match serde_json::from_str::<JsonArg>(&new_arg) {
471 Ok(json_arg) => {
472 if let Ok(named_arg) = NamedArg::try_from(json_arg.clone()) {
473 args.insert_cl_value(named_arg.name(), named_arg.cl_value().clone());
475 }
476 }
477 Err(_) => {
478 let _ = casper_client::cli::insert_arg(&new_arg, args);
480 }
481 }
482 args
483}
484
485#[cfg(test)]
486mod tests {
487 use super::*;
488 use casper_types::U256;
489
490 #[test]
491 fn test_cl_value_to_json() {
492 let cl_value = CLValue::from_t((1, 2, 3)).unwrap();
493 let json_value = cl_value_to_json(&cl_value).unwrap();
494 assert_eq!(
495 json_value,
496 Value::Array(vec![
497 Value::Number(1.into()),
498 Value::Number(2.into()),
499 Value::Number(3.into())
500 ])
501 );
502 }
503
504 #[test]
505 fn test_get_current_timestamp() {
506 let timestamp = Some("1234567890".to_string());
507 let current_timestamp = get_current_timestamp(timestamp);
508 assert_eq!(¤t_timestamp, "1970-01-15T06:56:07Z");
509 }
510
511 #[test]
512 fn test_get_blake2b_hash() {
513 let metadata = "some metadata";
514 let hash = get_blake2b_hash(metadata);
515 assert_eq!(
516 &hash,
517 "767de9efccc76bc0eef85ea81fcaa56dc7047e660c74b3dc39f84ab8c4931c0d"
518 );
519 }
520
521 #[test]
522 fn test_get_ttl_or_default() {
523 let ttl = Some("1h".to_string());
524 let ttl_value = get_ttl_or_default(ttl.as_deref());
525 assert_eq!(ttl_value, "1h".to_string());
526
527 let default_ttl = get_ttl_or_default(None);
528 assert_eq!(default_ttl, DEFAULT_TTL.to_string());
529 }
530
531 #[test]
532 fn test_parse_timestamp() {
533 let valid_timestamp = "2023-11-06T12:00:00Z";
534 let parsed_timestamp = parse_timestamp(valid_timestamp);
535 assert!(parsed_timestamp.is_ok());
536
537 let invalid_timestamp = "invalid_timestamp";
538 let parsed_timestamp = parse_timestamp(invalid_timestamp);
539 assert!(parsed_timestamp.is_err());
540 }
541
542 #[test]
543 fn test_parse_ttl() {
544 let valid_ttl = "1h";
545 let parsed_ttl = parse_ttl(valid_ttl);
546 assert!(parsed_ttl.is_ok());
547
548 let invalid_ttl = "invalid_ttl";
549 let parsed_ttl = parse_ttl(invalid_ttl);
550 assert!(parsed_ttl.is_err());
551 }
552
553 #[test]
554 fn test_get_gas_price_or_default() {
555 let gas_price = Some(100);
556 let price = get_gas_price_or_default(gas_price);
557 assert_eq!(price, 100);
558
559 let default_price = get_gas_price_or_default(None);
560 assert_eq!(default_price, DEFAULT_GAS_PRICE);
561 }
562
563 #[test]
564 fn test_get_str_or_default() {
565 let input_str = Some("test_string".to_string());
566 let result = get_str_or_default(input_str.as_ref());
567 assert_eq!(result, "test_string");
568
569 let default_str: Option<String> = None;
570 let result = get_str_or_default(default_str.as_ref());
571 assert_eq!(result, "");
572 }
573
574 #[test]
575 fn test_secret_key_generate() {
576 let result = secret_key_generate();
578
579 assert!(result.is_ok());
581 let secret_key = result.unwrap();
582 assert_eq!(&secret_key.to_string(), "SecretKey::Ed25519");
583 }
584
585 #[test]
586 fn test_secret_key_secp256k1_generate() {
587 let result = secret_key_secp256k1_generate();
589
590 assert!(result.is_ok());
592 let secret_key = result.unwrap();
593 assert_eq!(&secret_key.to_string(), "SecretKey::Secp256k1");
594 }
595
596 #[test]
597 fn test_secret_key_from_pem() {
598 let pem_key = "-----BEGIN PRIVATE KEY-----\nTEST\n-----END PRIVATE KEY-----";
599 let result = secret_key_from_pem(pem_key);
600 assert!(result.is_err());
601 let pem_key =
602 "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEII8ULlk1CJ12ZQ+bScjBt/IxMAZNggClWqK56D1/7CbI\n-----END PRIVATE KEY-----";
603 let result = secret_key_from_pem(pem_key);
604 assert!(result.is_ok());
605 assert_eq!(&result.unwrap().to_string(), "SecretKey::Ed25519");
606 }
607
608 #[test]
609 fn test_public_key_from_secret_key() {
610 let pem_key = "-----BEGIN PRIVATE KEY-----\nTEST\n-----END PRIVATE KEY-----";
611 let result = public_key_from_secret_key(pem_key);
612 assert!(result.is_err());
613 let pem_key =
614 "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEII8ULlk1CJ12ZQ+bScjBt/IxMAZNggClWqK56D1/7CbI-----END PRIVATE KEY-----";
615 let result = public_key_from_secret_key(pem_key);
616 assert!(result.is_ok());
617 assert_eq!(
618 result.unwrap(),
619 "01aff5c18a954604dd27d139d8e0cfc533ac3d53784d76c7a7ac5ff4039510fdf6"
620 );
621 }
622
623 #[test]
624 fn test_hex_to_uint8_vec() {
625 let hex_string = "0161e40005434ba3cd9a791a2827f5fa3ee514d1475fe72b2823cbaac9c3c71483";
626 let result = hex_to_uint8_vec(hex_string);
627 assert_eq!(
628 result,
629 vec![
630 1, 97, 228, 0, 5, 67, 75, 163, 205, 154, 121, 26, 40, 39, 245, 250, 62, 229, 20,
631 209, 71, 95, 231, 43, 40, 35, 203, 170, 201, 195, 199, 20, 131
632 ]
633 );
634 }
635
636 #[test]
637 fn test_hex_to_string() {
638 let hex_string = "48656c6c6f20436173706572";
639 let result = hex_to_string(hex_string);
640 assert_eq!(result, "Hello Casper");
641 }
642
643 #[test]
644 fn test_motes_to_cspr() {
645 let motes = "1000000000";
646 let result = motes_to_cspr(motes).unwrap();
647 assert_eq!(result, "1");
648 }
649
650 #[test]
651 fn test_json_pretty_print() {
652 #[derive(Serialize, Clone)]
653 struct TestData {
654 age: i32,
655 name: String,
656 }
657
658 let data = TestData {
659 age: 42,
660 name: "Joe".to_string(),
661 };
662
663 let result = json_pretty_print(data.clone(), None).unwrap();
664 assert_eq!(result, "{\"age\":42,\"name\":\"Joe\"}");
665
666 let result = json_pretty_print(data.clone(), Some(Verbosity::Low)).unwrap();
667 assert_eq!(result, "{\"age\":42,\"name\":\"Joe\"}");
668
669 let result = json_pretty_print(data.clone(), Some(Verbosity::Medium)).unwrap();
670 assert_eq!(result, "{\n \"age\": 42,\n \"name\": \"Joe\"\n}");
671
672 let result = json_pretty_print(data, Some(Verbosity::High)).unwrap();
673 assert_eq!(result, "{\n \"age\": 42,\n \"name\": \"Joe\"\n}");
674 }
675
676 #[test]
677 fn test_insert_arg_simple() {
678 let mut args = RuntimeArgs::new();
679 let new_arg = "message:String='Hello Casper";
680 let result_args = insert_arg(&mut args, new_arg.to_string());
681 assert_eq!(result_args.len(), 1);
682 let cl_value = result_args.get("message").unwrap();
683 let json = cl_value_to_json(cl_value).unwrap();
684 let expexted_json = Value::String("Hello Casper".to_string());
685 assert_eq!(json, expexted_json);
686 }
687
688 #[test]
689 fn test_insert_arg_json() {
690 let mut args = RuntimeArgs::new();
691 let arg_json = r#"{"name": "bar", "type": "U256", "value": 1}"#; let result_args = insert_arg(&mut args, arg_json.to_string());
693 assert_eq!(result_args.len(), 1);
694 let cl_value = result_args.get("bar").unwrap();
695 let json = cl_value_to_json(cl_value).unwrap();
696 let expexted_json = Value::String("1".to_string());
697 assert_eq!(json, expexted_json);
698 }
699
700 #[test]
701 pub fn test_make_dictionary_item_key() {
702 let key = Key::from_formatted_str(
703 "account-hash-e11bfffe63bf899ea07117af8a2bb43ef0078c0e38ebee6b6cb0b0e39c233538",
704 )
705 .unwrap();
706 let value = U256::from(1);
707 let dictionary_item_key = make_dictionary_item_key(&key, &value);
708 assert_eq!(
709 dictionary_item_key,
710 "145f6211a24c0a8af16b47e7aa58431ea25172eb402903b3c25ac92b9784c7a9".to_string()
711 );
712 let key = Key::from_formatted_str(
713 "account-hash-813428ce1a9805f1087db07e6017c6c4f5af0ee78a05591bb6577763e89b4f1f",
714 )
715 .unwrap();
716 let value = Key::from_formatted_str(
717 "account-hash-e11bfffe63bf899ea07117af8a2bb43ef0078c0e38ebee6b6cb0b0e39c233538",
718 )
719 .unwrap();
720 let dictionary_item_key = make_dictionary_item_key(&key, &value);
721 assert_eq!(
722 dictionary_item_key,
723 "1e26dc82db208943c3785c0e11b9d78b9c408fee748c78dda5a5d016840dedca".to_string()
724 );
725 }
726
727 #[test]
728 fn test_get_base64_key_from_account_hash() {
729 let input_hash =
731 "account-hash-b485c074cef7ccaccd0302949d2043ab7133abdb14cfa87e8392945c0bd80a5f";
732 let expected_output = "ALSFwHTO98yszQMClJ0gQ6txM6vbFM+ofoOSlFwL2Apf";
733
734 let result = get_base64_key_from_account_hash(input_hash).unwrap();
736
737 assert_eq!(result, expected_output.to_string());
739 }
740
741 #[test]
742 fn test_get_base64_key_from_key_hash() {
743 let input_hash = "hash-b485c074cef7ccaccd0302949d2043ab7133abdb14cfa87e8392945c0bd80a5f";
745 let expected_output = "AbSFwHTO98yszQMClJ0gQ6txM6vbFM+ofoOSlFwL2Apf";
746
747 let result = get_base64_key_from_key_hash(input_hash).unwrap();
749
750 assert_eq!(result, expected_output.to_string());
752 }
753}