use ripemd::Ripemd160;
use sha2::{Digest, Sha256};
pub fn compute_contract_hash(sender_le: [u8; 20], nef_checksum: u32, name: &str) -> [u8; 20] {
let mut script = Vec::new();
script.push(0x38); emit_push_bytes(&mut script, &sender_le);
emit_push_u32(&mut script, nef_checksum);
emit_push_bytes(&mut script, name.as_bytes());
hash160(&script)
}
pub fn parse_uint160_hex_be(value: &str) -> Result<[u8; 20], String> {
let trimmed = value.trim();
let without_prefix = trimmed
.strip_prefix("0x")
.or_else(|| trimmed.strip_prefix("0X"))
.unwrap_or(trimmed);
if without_prefix.len() != 40 {
return Err(format!(
"expected 40 hex characters for UInt160 (got {})",
without_prefix.len()
));
}
let bytes_be = hex::decode(without_prefix)
.map_err(|err| format!("invalid hex UInt160 '{value}': {err}"))?;
if bytes_be.len() != 20 {
return Err(format!(
"expected 20 bytes for UInt160 (got {})",
bytes_be.len()
));
}
let mut le = [0u8; 20];
for (dst, src) in le.iter_mut().zip(bytes_be.into_iter().rev()) {
*dst = src;
}
Ok(le)
}
pub fn format_uint160_hex_be(value_le: &[u8; 20]) -> String {
let be: Vec<u8> = value_le.iter().rev().copied().collect();
format!("0x{}", hex::encode(be))
}
fn hash160(data: &[u8]) -> [u8; 20] {
let sha = Sha256::digest(data);
let digest = Ripemd160::digest(sha);
let mut out = [0u8; 20];
out.copy_from_slice(&digest[..20]);
out
}
fn emit_push_bytes(script: &mut Vec<u8>, data: &[u8]) {
if data.len() <= u8::MAX as usize {
script.push(0x0C); script.push(data.len() as u8);
} else if data.len() <= u16::MAX as usize {
script.push(0x0D); script.extend_from_slice(&(data.len() as u16).to_le_bytes());
} else {
script.push(0x0E); script.extend_from_slice(&(data.len() as u32).to_le_bytes());
}
script.extend_from_slice(data);
}
fn emit_push_u32(script: &mut Vec<u8>, value: u32) {
if value == 0 {
script.push(0x10); return;
}
if value <= 16 {
script.push(0x10 + value as u8);
return;
}
if value <= i8::MAX as u32 {
script.push(0x00); script.push(value as u8);
return;
}
if value <= i16::MAX as u32 {
script.push(0x01); script.extend_from_slice(&(value as i16).to_le_bytes());
return;
}
if value <= i32::MAX as u32 {
script.push(0x02); script.extend_from_slice(&(value as i32).to_le_bytes());
return;
}
script.push(0x03); script.extend_from_slice(&(value as i64).to_le_bytes());
}