use bip32::XPub;
use hex::decode;
use sha3::{Digest, Keccak256};
#[derive(Clone, Debug)]
pub struct Account {
pub address: String,
pub public_key: XPub,
}
impl Account {
pub fn from_extended_public_key(extended_public_key: &XPub) -> Result<Self, String> {
let address = extended_public_key_to_address(extended_public_key)?;
Ok(Account {
address: add0x(address),
public_key: extended_public_key.to_owned(),
})
}
}
pub fn assert_is_valid_hex_address(value: &str) -> Result<(), String> {
let unprefixed = remove0x(value);
assert_is_hex(&unprefixed)?;
if unprefixed.len() != 40 {
return Err(format!(
"String passed into assert_is_valid_hex_address is {} hex chars long instead of 40.",
value.len()
));
}
Ok(())
}
pub fn assert_is_hex(value: &str) -> Result<(), String> {
match decode(value) {
Ok(_) => Ok(()),
Err(e) => Err(e.to_string()),
}
}
pub fn remove0x(value: &str) -> &str {
match value.starts_with("0x") {
true => &value[2..],
_ => value,
}
}
pub fn add0x(value: String) -> String {
match value.starts_with("0x") {
true => value,
_ => format!("0x{}", value),
}
}
pub fn extended_public_key_to_address(extended_public_key: &XPub) -> Result<String, String> {
let address = keccak_hash(&extended_public_key.to_bytes());
Ok(address[(address.len() - 40)..].to_string())
}
fn keccak_hash<T>(data: &T) -> String
where
T: ?Sized + AsRef<[u8]>,
{
let mut hasher = Keccak256::new();
hasher.update(data);
let result = hasher.finalize();
hex::encode(result)
}