use std::convert::TryInto;
use bigdecimal::BigDecimal;
use num::BigUint;
use zksync_basic_types::{Address, H256, U256};
pub fn u256_to_big_decimal(value: U256) -> BigDecimal {
let mut u32_digits = vec![0_u32; 8];
for (i, &u64_digit) in value.0.iter().enumerate() {
u32_digits[2 * i] = u64_digit as u32;
u32_digits[2 * i + 1] = (u64_digit >> 32) as u32;
}
let value = BigUint::new(u32_digits);
BigDecimal::new(value.into(), 0)
}
fn biguint_to_u256(value: BigUint) -> U256 {
let bytes = value.to_bytes_le();
U256::from_little_endian(&bytes)
}
pub fn bigdecimal_to_u256(value: BigDecimal) -> U256 {
let bigint = value.with_scale(0).into_bigint_and_exponent().0;
biguint_to_u256(bigint.to_biguint().unwrap())
}
fn ensure_chunkable(bytes: &[u8]) {
assert!(
bytes.len() % 32 == 0,
"Bytes must be divisible by 32 to split into chunks"
);
}
pub fn h256_to_u256(num: H256) -> U256 {
U256::from_big_endian(num.as_bytes())
}
pub fn address_to_h256(address: &Address) -> H256 {
let mut buffer = [0u8; 32];
buffer[12..].copy_from_slice(address.as_bytes());
H256(buffer)
}
pub fn address_to_u256(address: &Address) -> U256 {
h256_to_u256(address_to_h256(address))
}
pub fn bytes_to_chunks(bytes: &[u8]) -> Vec<[u8; 32]> {
ensure_chunkable(bytes);
bytes
.chunks(32)
.map(|el| {
let mut chunk = [0u8; 32];
chunk.copy_from_slice(el);
chunk
})
.collect()
}
pub fn be_chunks_to_h256_words(chunks: Vec<[u8; 32]>) -> Vec<H256> {
chunks.into_iter().map(|el| H256::from_slice(&el)).collect()
}
pub fn bytes_to_be_words(vec: Vec<u8>) -> Vec<U256> {
ensure_chunkable(&vec);
vec.chunks(32).map(U256::from_big_endian).collect()
}
pub fn be_words_to_bytes(words: &[U256]) -> Vec<u8> {
words
.iter()
.flat_map(|w| {
let mut bytes = [0u8; 32];
w.to_big_endian(&mut bytes);
bytes
})
.collect()
}
pub fn u256_to_h256(num: U256) -> H256 {
let mut bytes = [0u8; 32];
num.to_big_endian(&mut bytes);
H256::from_slice(&bytes)
}
pub fn u256_to_account_address(value: &U256) -> Address {
let mut bytes = [0u8; 32];
value.to_big_endian(&mut bytes);
Address::from_slice(&bytes[12..])
}
pub fn h256_to_account_address(value: &H256) -> Address {
Address::from_slice(&value.as_bytes()[12..])
}
pub fn be_bytes_to_safe_address(bytes: &[u8]) -> Option<Address> {
if bytes.len() < 20 {
return None;
}
let (zero_bytes, address_bytes) = bytes.split_at(bytes.len() - 20);
if zero_bytes.iter().any(|b| *b != 0) {
None
} else {
Some(Address::from_slice(address_bytes))
}
}
pub fn h256_to_u32(value: H256) -> u32 {
let be_u32_bytes: [u8; 4] = value[28..].try_into().unwrap();
u32::from_be_bytes(be_u32_bytes)
}
pub fn u32_to_h256(value: u32) -> H256 {
let mut result = [0u8; 32];
result[28..].copy_from_slice(&value.to_be_bytes());
H256(result)
}
pub fn u256_to_bytes_be(value: &U256) -> Vec<u8> {
let mut bytes = vec![0u8; 32];
value.to_big_endian(bytes.as_mut_slice());
bytes
}
#[cfg(test)]
mod test {
use num::BigInt;
use rand::{rngs::StdRng, Rng, SeedableRng};
use super::*;
#[test]
fn test_u256_to_bigdecimal() {
const RNG_SEED: u64 = 123;
let mut rng = StdRng::seed_from_u64(RNG_SEED);
for _ in 0..10_000 {
let value: u64 = rng.gen();
let expected = BigDecimal::from(value);
assert_eq!(u256_to_big_decimal(value.into()), expected);
}
for _ in 0..10_000 {
let u64_digits: [u64; 4] = rng.gen();
let value = u64_digits
.iter()
.enumerate()
.map(|(i, &digit)| U256::from(digit) << (i * 64))
.fold(U256::zero(), |acc, x| acc + x);
let expected_value = u64_digits
.iter()
.enumerate()
.map(|(i, &digit)| BigInt::from(digit) << (i * 64))
.fold(BigInt::from(0), |acc, x| acc + x);
assert_eq!(
u256_to_big_decimal(value),
BigDecimal::new(expected_value, 0)
);
}
}
#[test]
fn test_bigdecimal_to_u256() {
let value = BigDecimal::from(100u32);
let expected = U256::from(100u32);
assert_eq!(bigdecimal_to_u256(value), expected);
let value = BigDecimal::new(BigInt::from(100), -2);
let expected = U256::from(10000u32);
assert_eq!(bigdecimal_to_u256(value), expected);
}
}