use base64::Engine;
use primitive_types::{H160, H256, U256};
use crate::{prelude::ScriptHash, TypeError};
pub fn parse_string_u64(u64_str: &str) -> Result<u64, TypeError> {
if let Some(stripped) = u64_str.strip_prefix("0x") {
u64::from_str_radix(stripped, 16).map_err(|e| {
TypeError::InvalidFormat(format!("Failed to parse hex u64 '{}': {}", u64_str, e))
})
} else {
u64_str.parse::<u64>().map_err(|e| {
TypeError::InvalidFormat(format!("Failed to parse decimal u64 '{}': {}", u64_str, e))
})
}
}
pub fn parse_string_u256(u256_str: &str) -> Result<U256, TypeError> {
if let Some(stripped) = u256_str.strip_prefix("0x") {
U256::from_str_radix(stripped, 16).map_err(|e| {
TypeError::InvalidFormat(format!("Failed to parse hex U256 '{}': {}", u256_str, e))
})
} else {
U256::from_dec_str(u256_str).map_err(|e| {
TypeError::InvalidFormat(format!("Failed to parse decimal U256 '{}': {}", u256_str, e))
})
}
}
pub fn parse_address(address: &str) -> Result<ScriptHash, TypeError> {
let has_0x = address.starts_with("0x");
let mut bytes = hex::decode(address.trim_start_matches("0x")).map_err(|e| {
TypeError::InvalidFormat(format!("Failed to decode address hex '{}': {}", address, e))
})?;
if bytes.len() > 20 {
return Err(TypeError::InvalidFormat(format!(
"Address hex is too long: {} bytes (max 20 bytes)",
bytes.len()
)));
}
if has_0x {
bytes.reverse();
}
let mut padded_bytes = [0_u8; 20];
padded_bytes[20 - bytes.len()..].copy_from_slice(&bytes);
Ok(ScriptHash::from_slice(&padded_bytes))
}
pub fn encode_string_h160(h160: &H160) -> String {
format!("{:?}", h160).to_owned()
}
pub fn parse_string_h160(h160_str: &str) -> Result<H160, TypeError> {
let bytes = hex::decode(h160_str.trim_start_matches("0x")).map_err(|e| {
TypeError::InvalidFormat(format!("Failed to decode H160 hex '{}': {}", h160_str, e))
})?;
if bytes.len() > 20 {
return Err(TypeError::InvalidFormat(format!(
"H160 hex is too long: {} bytes (max 20 bytes)",
bytes.len()
)));
}
let mut padded_bytes = [0_u8; 20];
padded_bytes[20 - bytes.len()..].copy_from_slice(&bytes);
Ok(H160::from_slice(&padded_bytes))
}
pub fn parse_string_h256(h256_str: &str) -> Result<H256, TypeError> {
let bytes = hex::decode(h256_str.trim_start_matches("0x")).map_err(|e| {
TypeError::InvalidFormat(format!("Failed to decode H256 hex '{}': {}", h256_str, e))
})?;
if bytes.len() > 32 {
return Err(TypeError::InvalidFormat(format!(
"The provided string is too long to be a valid H256: {} (length: {})",
h256_str,
bytes.len()
)));
}
let mut padded_bytes = [0_u8; 32];
padded_bytes[32 - bytes.len()..].copy_from_slice(&bytes);
Ok(H256::from_slice(&padded_bytes))
}
pub fn encode_string_h256(h256: &H256) -> String {
format!("{:?}", h256).to_owned()
}
pub fn encode_string_u256(u256: &U256) -> String {
format!("0x{:x}", u256).to_owned()
}
pub fn encode_vec_string_vec_u256(item: Vec<U256>) -> Vec<String> {
item.iter().map(encode_string_u256).collect()
}
pub fn parse_vec_string_vec_u256(item: Vec<String>) -> Result<Vec<U256>, TypeError> {
let mut result = Vec::with_capacity(item.len());
for x in item {
result.push(parse_string_u256(&x)?);
}
Ok(result)
}
pub fn h256_to_u256(item: H256) -> U256 {
U256::from_big_endian(item.as_bytes())
}
pub fn bytes_to_string(mybytes: &[u8]) -> String {
format!("0x{}", hex::encode(mybytes))
}
pub fn string_to_bytes(mystring: &str) -> Result<Vec<u8>, TypeError> {
if mystring.starts_with("0x") {
let mystring = mystring.trim_start_matches("0x");
hex::decode(mystring).map_err(|e| {
TypeError::InvalidFormat(format!("Failed to decode hex string '{}': {}", mystring, e))
})
} else {
Err(TypeError::InvalidFormat(format!(
"String '{}' does not start with '0x' prefix",
mystring
)))
}
}
pub fn u256_sqrt(input: &U256) -> U256 {
if *input < 2.into() {
return *input;
}
let mut x: U256 = (input + U256::one()) >> 1;
let mut y = *input;
while x < y {
y = x;
x = (input / x + x) >> 1;
}
y
}
pub fn u256_min(x: U256, y: U256) -> U256 {
if x > y {
y
} else {
x
}
}
pub fn vec_to_array32(vec: Vec<u8>) -> Result<[u8; 32], TypeError> {
if vec.len() != 32 {
return Err(TypeError::InvalidData(
"Vector does not contain exactly 32 elements".to_string(),
));
}
let mut array = [0u8; 32];
let bytes = &vec[..array.len()]; array.copy_from_slice(bytes); Ok(array)
}
pub fn var_size(value: usize) -> usize {
let mut v = value;
let mut bytes = 0;
while v > 0 {
v >>= 8;
bytes += 1;
}
if bytes == 0 {
1
} else {
bytes
}
}
pub trait ToBase58 {
fn to_base58(&self) -> String;
}
impl ToBase58 for [u8] {
fn to_base58(&self) -> String {
bs58::encode(self).into_string()
}
}
pub trait ToBase64 {
fn to_base64(&self) -> String;
}
impl ToBase64 for [u8] {
fn to_base64(&self) -> String {
base64::engine::general_purpose::STANDARD.encode(self)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_bytes_to_string() {
let mybytes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let bytestring = bytes_to_string(&mybytes);
let orig_bytestring = "0x0102030405060708090a";
assert_eq!(&bytestring, orig_bytestring);
let error_bytestring = "0102030405060708090a";
let error_result = string_to_bytes(error_bytestring);
assert!(error_result.is_err());
let ok_mybytes = string_to_bytes(orig_bytestring).expect("Should decode valid hex string");
assert_eq!(&mybytes[..], &ok_mybytes[..]);
}
}