use alloc::vec::Vec;
use miden_core::utils::bytes_to_packed_u32_elements;
use miden_protocol::Felt;
use miden_protocol::asset::FungibleAsset;
use primitive_types::U256;
use thiserror::Error;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
pub enum EthAmountError {
#[error("amount overflow: value doesn't fit in target type")]
Overflow,
#[error("scaling factor too large: maximum is 18")]
ScaleTooLarge,
#[error("scaled value doesn't fit in u64")]
ScaledValueDoesNotFitU64,
#[error("scaled value exceeds the maximum fungible token amount")]
ScaledValueExceedsMaxFungibleAmount,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct EthAmount([u8; 32]);
impl EthAmount {
pub fn new(bytes: [u8; 32]) -> Self {
Self(bytes)
}
pub fn from_uint_str(s: &str) -> Result<Self, EthAmountError> {
let value = U256::from_dec_str(s).map_err(|_| EthAmountError::Overflow)?;
Ok(Self(value.to_big_endian()))
}
pub fn to_u256(&self) -> U256 {
U256::from_big_endian(&self.0)
}
#[cfg(any(test, feature = "testing"))]
pub fn from_u256(value: U256) -> Self {
Self(value.to_big_endian())
}
pub fn to_elements(&self) -> Vec<Felt> {
bytes_to_packed_u32_elements(&self.0)
}
pub const fn as_bytes(&self) -> &[u8; 32] {
&self.0
}
}
const MAX_SCALING_FACTOR: u32 = 18;
fn pow10_u64(scale: u32) -> Result<u64, EthAmountError> {
if scale > MAX_SCALING_FACTOR {
return Err(EthAmountError::ScaleTooLarge);
}
Ok(10_u64.pow(scale))
}
impl EthAmount {
pub fn scale_to_token_amount(&self, scale_exp: u32) -> Result<Felt, EthAmountError> {
let x = self.to_u256();
let scale = U256::from(pow10_u64(scale_exp)?);
let y_u256 = x / scale;
let y_u64: u64 = y_u256.try_into().map_err(|_| EthAmountError::ScaledValueDoesNotFitU64)?;
if y_u64 > FungibleAsset::MAX_AMOUNT {
return Err(EthAmountError::ScaledValueExceedsMaxFungibleAmount);
}
let y_felt = Felt::try_from(y_u64).expect("scaled value must fit into canonical Felt");
Ok(y_felt)
}
}